分布式限流——Redis + Lua实现滑动窗口算法

Zset(有序集合)在Redis中用来实现滑动窗口限流的主要思路是利用其自动排序和可过期成员的特点:

  1. 初始化及数据结构选择

    • 为需要限流的接口或服务创建一个唯一的键(key)对应一个Zset。
    • Zset中的每个成员通常是请求的唯一标识符(如UUID或其他唯一字符串),用于区分不同的请求。
    • Zset的score字段用来存储每个请求的时间戳,由于Redis中的score支持浮点数,通常会存储Unix时间戳(秒级或毫秒级精度)。
  2. 添加请求记录

    • 当有新的请求到来时,将当前时间戳作为score,添加到Zset中,同时成员可以是任意唯一标识符,或者是省略,仅保留score的有序排列。
  3. 检查窗口内的请求数量

    • 根据限流策略(比如每分钟100次),计算出当前时间戳对应的窗口开始时间(当前时间减去窗口长度)。
    • 使用Zset的ZCARD命令,查找score在窗口范围内的元素数量。
    • 如果数量超过设定的阈值,则拒绝新请求。
  4. 移除过期请求记录

    • 可以结合Zset的过期功能(TTL)来自动清理超时的请求记录,也可以在每次处理请求时手动清理窗口开始时间之前的所有记录,这样能确保Zset只包含当前窗口内的请求。
  5. 原子操作与并发控制

    • 在高并发场景下,为了保证限流逻辑的正确执行,可以通过编写Lua脚本来实现一系列操作的原子性执行,避免因并发问题造成的计数不准确。

通过以上步骤,Zset能够有效地维护一个时间窗口内的请求数量,从而达到限流的目的,确保服务在高峰期不会因为过多请求而导致性能瓶颈。

以下是一个基于Redis Zset实现滑动窗口限流的Lua脚本示例,假设我们希望限制在过去的10秒内某个服务的请求数量不超过100个:

Lua 复制代码
-- KEYS[1] 是服务的唯一标识符
-- ARGV[1] 是窗口大小(秒)
-- ARGV[2] 是限流阈值(请求次数)
-- ARGV[3] 是当前请求的时间戳(毫秒级)

-- 获取窗口开始时间戳
local windowStart = tonumber(ARGV[3]) - tonumber(ARGV[1]) * 1000

-- 移除窗口开始时间之前的请求记录
redis.call('ZREMRANGEBYSCORE', KEYS[1], 0, windowStart)

-- 添加当前请求记录
redis.call('ZADD', KEYS[1], ARGV[3], '')

-- 获取窗口内的请求数量
local requestCount = redis.call('ZCARD', KEYS[1])

-- 判断是否超过限流阈值
if requestCount > tonumber(ARGV[2]) then
    -- 如果超过阈值,删除刚添加的请求记录(模拟请求被拒绝)
    redis.call('ZREM', KEYS[1], '')
    return 0 -- 返回0表示请求被限流
else
    return 1 -- 返回1表示请求被允许
end
相关推荐
Code apprenticeship1 小时前
怎么利用Redis实现延时队列?
数据库·redis·缓存
百度智能云技术站1 小时前
广告投放系统成本降低 70%+,基于 Redis 容量型数据库 PegaDB 的方案设计和业务实践
数据库·redis·oracle
装不满的克莱因瓶1 小时前
【Redis经典面试题六】Redis的持久化机制是怎样的?
java·数据库·redis·持久化·aof·rdb
Java程序之猿1 小时前
微服务分布式(一、项目初始化)
分布式·微服务·架构
来一杯龙舌兰2 小时前
【RabbitMQ】RabbitMQ保证消息不丢失的N种策略的思想总结
分布式·rabbitmq·ruby·持久化·ack·消息确认
节点。csn3 小时前
Hadoop yarn安装
大数据·hadoop·分布式
黄名富5 小时前
Redis 附加功能(二)— 自动过期、流水线与事务及Lua脚本
java·数据库·redis·lua
NiNg_1_2345 小时前
基于Hadoop的数据清洗
大数据·hadoop·分布式
G_whang5 小时前
centos7下docker 容器实现redis主从同步
redis·docker·容器
.生产的驴6 小时前
SpringBoot 对接第三方登录 手机号登录 手机号验证 微信小程序登录 结合Redis SaToken
java·spring boot·redis·后端·缓存·微信小程序·maven