限流

Redis 限流

使用 Redis 实现 API 限流,保护您的服务免受滥用。学习滑动窗口、令牌桶和固定窗口模式。

真实业务场景

一个公共 API 服务于 10,000 名开发者。如果没有限流,单个行为不端的客户端可能会耗尽服务器资源,导致服务降级。你需要强制执行每个 API Key 每分钟 100 次请求的限制,并在超出限制时提供明确反馈。Redis 提供原子计数器和过期机制,可高效地大规模实现此功能。

架构图

API 请求 + API Key 头部
API 网关 / 中间件
↓ 检查速率限制
┌──────────────────────────────────┐
│ Redis (速率限制计数器) │
│ ┌────────────────────────────┐ │
│ │ ratelimit:{apiKey}:{window} │ │
│ │ Value: 请求计数 │ │
│ │ TTL: 窗口持续时间 │ │
│ └────────────────────────────┘ │
└──────────────────────────────────┘
↓ 低于限制 → 转发
后端服务

关键命令解析

性能分析

INCR 是 O(1)——无论请求量如何,时间恒定。每次请求增加约 0.1ms 开销。
固定窗口: 每个用户每个窗口 1 个 key。1万用户 × 1 key = 1万 keys。内存占用极小。
滑动窗口 (Sorted Set): 每次操作 O(log N)。更准确但成本稍高。适用于每个窗口最多约 1000 个请求的场景。
Redis 单实例每秒可处理 10万+ INCR——足以满足大多数限流需求,无需集群。

常见陷阱

固定窗口临界突发: 用户可以在 :59 发送 100 个请求,在 :00 再发送 100 个,实际上 2 秒内发送了 200 个。使用滑动窗口或滑动日志来防止这种情况。
INCR + EXPIRE 竞态条件: 如果 EXPIRE 在 INCR 后失败,key 将永久存在且计数器从未重置。务必使用 MULTI/EXEC 或 Lua 脚本。
分布式系统中的时钟偏差: 如果应用服务器时钟不同步,窗口边界会有差异。使用 Redis 服务器时间 (TIME 命令) 作为事实来源。
缺少限流 Header: 始终返回 X-RateLimit-Limit, X-RateLimit-Remaining 和 Retry-After 头部,以便客户端可以自我节流。

最佳实践

使用 Lua 脚本进行原子检查并递增,以消除竞态条件。
实施多级限流:每秒突发限制 + 每分钟持续限制。
超出限制时返回 HTTP 429 和 Retry-After 头部。
对公平性要求高的面向用户的 API 使用滑动窗口。
在每个请求的响应头中添加限流信息,而不仅仅是拒绝时。
对于需要允许短时突发超过平均速率的 API,考虑使用令牌桶算法。

可运行演示

Redis Demo
Click "Step" or "Run All" to execute commands...

在我们的 Redis 在线编辑器中尝试这些命令