Skip to content

计数器与限流

实现方案

1. 固定窗口

lua
-- 固定1分钟内100次
local key = "rate:limit:" .. user_id .. ":" .. os.time() // 60
local count = redis.incr(key)
if count == 1 then
    redis.expire(key, 60)
end
return count <= 100

2. 滑动窗口

lua
-- 滑动窗口去重
local key = "rate:window:" .. user_id
local now = os.time()
redis.zadd(key, now, now .. ":" .. math.random())
redis.zremrangebyscore(key, 0, now - 60)  -- 移除60秒前的
local count = redis.zcard(key)
return count <= 100

3. 令牌桶

lua
local key = "rate:bucket:" .. user_id
local last = tonumber(redis.hget(key, "last") or 0)
local tokens = tonumber(redis.hget(key, "tokens") or 10)
local now = os.time()
local delta = (now - last) * 1.0  -- 每秒1个令牌

tokens = math.min(10, tokens + delta)
if tokens >= 1 then
    tokens = tokens - 1
    redis.hset(key, "last", now)
    redis.hset(key, "tokens", tokens)
    return true
else
    return false
end

分布式限流

简单实现

lua
-- 多Redis实例汇总
local count = 0
for _, ip in ipairs(redis_ips) do
    count = count + redis.call("get", "rate:" .. ip)
end
return count <= limit

面试考点

Q: 固定窗口vs滑动窗口?

  • 固定窗口:简单,可能有临界突刺
  • 滑动窗口:精确,无突刺

Q: 令牌桶适合什么场景?

适合需要平滑限流的场景,允许一定程度的突发流量。

最后更新: