← 返回题目列表

Redis 如何实现限流?固定窗口、滑动窗口和令牌桶怎么选?

高频 中等 第 12 / 36 题 更新于 2026/07/29
Redis限流Lua令牌桶

简化版

Redis 实现限流常见有固定窗口计数、滑动窗口、令牌桶和漏桶。固定窗口简单但边界突刺明显;滑动窗口更平滑但成本更高;令牌桶允许一定突发,适合接口限流。线上通常用 Redis + Lua 保证判断和更新原子性。

详细版

常见 Redis 限流方案:

  • 固定窗口:INCR + EXPIRE,简单高效。
  • 滑动窗口:ZSet 存请求时间戳,按时间范围统计。
  • 令牌桶:按速率生成令牌,请求消耗令牌。
  • 漏桶:请求以固定速率流出,更强调平滑。

固定窗口适合粗粒度限制,如每分钟最多 100 次;滑动窗口适合更精确控制;令牌桶适合允许瞬时突发但限制平均速率的接口。

关键点是原子性和过期清理。多条 Redis 命令要用 Lua 或事务,否则并发下可能超发。

完整版教学

一、Redis 限流要解决什么

限流的目标是保护系统容量,避免某个用户、IP、接口或租户在短时间内打爆后端。Redis 适合做分布式限流,因为它快、集中、支持原子脚本。

限流不是简单拒绝请求,还要定义维度、窗口、阈值、突发能力和降级动作。例如“用户 1 分钟最多 60 次”和“接口全局每秒最多 1000 次”是不同限流器。

记忆钩子:限流先定“谁、多久、多少、超了怎么办”,再谈算法。

二、固定窗口最简单

固定窗口用 key 表示某个时间段的计数。请求来时 INCR,第一次设置过期时间,超过阈值就拒绝。

key = rate:user:100:202607291705
INCR key
EXPIRE key 60

如果限制每分钟 100 次,实现很简单,性能也好。但问题是窗口边界突刺:用户在 00:59 发 100 次,01:00 又发 100 次,两秒内可能通过 200 次。

三、滑动窗口更精确

滑动窗口通常用 ZSet 存请求时间戳。每次请求先删除窗口外的数据,再统计窗口内数量,未超过阈值则加入当前时间戳。

ZREMRANGEBYSCORE key 0 now-60000
ZCARD key
ZADD key now requestId
EXPIRE key 60

它能避免固定窗口边界突刺,但每次请求要维护 ZSet,成本比 INCR 高。高 QPS 场景要注意内存和 CPU。

四、令牌桶适合允许突发

令牌桶按固定速率生成令牌,桶有容量上限。请求来了,如果有令牌就通过并消耗一个;没有令牌就拒绝或排队。

假设生成速率是每秒 10 个,桶容量 50 个。系统空闲 5 秒后桶满,下一秒可以允许最多 50 个突发请求,但长期平均仍接近每秒 10 个。

new_tokens = min(capacity, old_tokens + elapsed * rate)
if new_tokens >= cost:
  allow and tokens = new_tokens - cost
else:
  reject

这个模型适合 API 限流,因为真实流量常有短暂突发。

五、漏桶强调平滑输出

漏桶可以理解为请求先进入桶,再按固定速率流出。桶满时新请求被拒绝。它比令牌桶更强调平滑,不太鼓励突发。

算法优点缺点
固定窗口简单高效边界突刺
滑动窗口更精确成本较高
令牌桶支持突发实现稍复杂
漏桶输出平滑突发友好性差

面试时可以结合业务选择:登录防刷可用固定/滑动窗口,开放 API 常用令牌桶。

六、为什么要用 Lua 保证原子性

限流通常包含读取、判断、写入、设置过期多个步骤。如果这些步骤分开执行,在并发下可能多个请求同时看到“未超过”,然后一起放行。

Lua 脚本在 Redis 中执行具有原子性,可以把判断和更新放在服务端一次完成。

客户端并发请求
   |
Redis Lua:读取状态 -> 计算 -> 写回 -> 返回 allow/reject

这样既减少网络往返,也避免并发超发。

七、线上限流要考虑降级体验

限流不是算法写完就结束。被限流后返回什么、是否重试、是否排队、是否按租户隔离,都影响用户体验和系统稳定性。

还要避免 Redis 自身成为瓶颈。全站所有请求都打一个限流 key,会产生热 Key;可以按用户、接口、地域拆维度,或者在网关本地做预限流,Redis 做全局校准。

本地限流挡一层 -> Redis 全局限流 -> 后端服务保护

八、常见误区与追问

  • 误区:固定窗口没有问题。 它有窗口边界突刺,精确限流场景要谨慎。
  • 误区:滑动窗口一定最好。 它更精确但成本更高,高 QPS 下要评估 ZSet 开销。
  • 误区:不用 Lua 也不会超发。 多命令组合在并发下可能出现竞态。
  • 追问:令牌桶和漏桶怎么选? 令牌桶允许突发,漏桶强调平滑输出。
  • 追问:限流 key 怎么设计? 按用户、IP、接口、租户、时间窗口组合,避免过粗或过细。
  • 追问:Redis 挂了限流怎么办? 根据业务选择 fail-open 或 fail-close,并配本地限流兜底。

九、加强记忆

记住“固定简单、滑动精确、令牌突发、漏桶平滑”。Redis 实现限流的灵魂是原子脚本和合理 key 维度。

答题时先讲算法取舍,再讲 Lua 原子性和热 Key 风险,会比只背 INCR + EXPIRE 更完整。