Zset应用之滑动窗口限流

滑动窗口限流的实现原理

滑动窗口限流 的核心是:​统计某个时间窗口内的请求数,若超过阈值则拒绝新请求。
用 Redis ZSet 实现的关键步骤:


1. 数据结构设计
  • ZSet Keyrate_limit:api1(示例)
  • member:请求唯一标识(如 UUID 或 IP+时间戳)
  • score:请求的时间戳(单位需一致,如秒或毫秒)

2. 限流逻辑(分步骤)

假设限制 ​60 秒内最多 100 次请求

步骤 1:删除时间窗口外的旧请求
# 删除 60 秒前的数据(当前时间戳为 1715000000)
ZREMRANGEBYSCORE rate_limit:api1 0 (1715000000-60)
  • (1715000000-60) 表示只保留最近 60 秒的请求(即 score >= 1715000000-60)。
  • 作用:清理过期数据,保证 ZSet 中仅存当前窗口内的请求。
步骤 2:统计当前窗口内的请求数
ZCARD rate_limit:api1
  • 若结果 >= 100,拒绝新请求;否则允许。
步骤 3:记录新请求
ZADD rate_limit:api1 1715000000 "request_id_123"
  • 将新请求的时间戳和唯一标识添加到 ZSet。
步骤 4:设置 ZSet 过期时间(可选)
EXPIRE rate_limit:api1 60
  • 避免 ZSet 无限增长(即使有步骤 1 的清理,也需兜底策略)。

3. 完整操作(原子性优化)

为保证 ​删除旧数据 + 统计 + 添加新数据 的原子性,建议用 ​Lua 脚本

local key = KEYS[1]
local now = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
local limit = tonumber(ARGV[3])
local request_id = ARGV[4]

-- 删除窗口外的旧数据
redis.call('ZREMRANGEBYSCORE', key, 0, now - window)

-- 统计当前请求数
local count = redis.call('ZCARD', key)
if count >= limit then
    return 0  -- 限流
else
    -- 添加新请求并刷新过期时间
    redis.call('ZADD', key, now, request_id)
    redis.call('EXPIRE', key, window)
    return 1  -- 放行
end

你可能感兴趣的:(java,数据库,服务器,算法,开发语言)