Redis 8.8 在 2026 年 5 月发布的开源版本中引入了一项关键能力:原生的窗口计数器速率限制器(window counter rate limiter)。这一特性通过全新的 INCREX 命令实现,标志着 Redis 在分布式限流场景下从 "脚本化 workaround" 迈向 "原生原子操作" 的重要转变。
从 Lua 脚本到原生命令的演进
在 Redis 8.8 之前,实现滑动窗口速率限制通常依赖 Lua 脚本配合 ZADD、ZREMRANGEBYSCORE 和 ZCARD 等命令组合。这种方案虽然功能完整,但存在两个明显短板:一是 Lua 脚本在 Redis 单线程模型中执行,复杂逻辑会阻塞其他命令;二是滑动窗口日志(sliding window log)需要存储每个请求的时间戳,内存开销随流量线性增长。
Redis 8.8 的 INCREX 命令将窗口计数器的核心逻辑内嵌到服务端,支持固定窗口、惰性重置固定窗口以及滑动窗口计数器三种变体。命令原型为:
INCREX key BYINT increment EX seconds UBOUND max [SATURATE] [ENX]
其中 ENX(Expire if Not eXists)选项尤为关键 —— 它确保过期时间仅在窗口创建时设置,后续请求不会重置 TTL,从而保证窗口边界的严格性。
滑动窗口计数器的内存优化原理
滑动窗口日志方案需要为每个请求记录精确时间戳,假设每秒 1000 请求、窗口 60 秒,就需要维护 60000 个时间戳条目。而滑动窗口计数器采用 "分桶聚合" 策略:将大窗口切分为多个子窗口(如 6 个 10 秒子窗口),每个子窗口仅维护一个计数器。
内存对比大致如下:
| 方案 | 每窗口存储量 | 60 秒窗口 / 1000 RPS |
|---|---|---|
| 滑动窗口日志 | 每个请求一个时间戳 | ~60000 个条目 |
| 滑动窗口计数器 | 子窗口数量个计数器 | ~6 个计数器 |
Redis 8.8 的原生实现进一步优化了计数器的存储结构,采用紧凑的整数编码,相比外部方案使用 Hash 或 Sorted Set 存储计数器,内存占用可降低 30-50%。
INCREX 的原子性优势与外部中间件对比
与基于外部中间件(如 Envoy、Nginx 限流模块或独立服务)的方案相比,Redis 原生速率限制器的核心优势在于原子性保证。
外部中间件方案通常遵循 "读取计数→判断→更新计数" 的三步流程,即使使用 Redis 也需要至少两次网络往返(GET 后 INCR),在高并发下存在竞态条件。而 INCREX 将 "检查边界" 和 "原子递增" 合并为单次操作:
- 请求到达时,Redis 原子地检查当前窗口计数是否超过
UBOUND - 若未超限,执行递增并返回新值和实际增量
- 若已超限,根据
SATURATE参数决定拒绝或部分接受
这种单命令原子性消除了竞态窗口,避免了 "超卖" 问题。同时,原生实现省去了 Lua 脚本的解析和执行开销,在高并发场景下延迟更稳定。
可落地的配置参数清单
基于 Redis 8.8 的 INCREX,以下是生产环境推荐的配置模式:
API 网关限流(每用户每分钟 100 请求):
INCREX ratelimit:user:{user_id} BYINT 1 EX 60 UBOUND 100 ENX
突发流量缓冲(每秒 10 请求,允许瞬时 20 请求):
INCREX ratelimit:burst:{client_id} BYINT 1 EX 1 UBOUND 20 SATURATE ENX
滑动窗口近似(60 秒窗口分 6 个子窗口):
需在应用层维护子窗口切换逻辑,每个子窗口使用独立的 INCREX key,查询时聚合最近 6 个窗口的计数。
关键参数说明:
EX/PX:窗口持续时间,建议比实际业务周期略长(如 65 秒对应 60 秒窗口),避免边界漂移UBOUND:硬上限,超过即拒绝SATURATE:启用后超限请求会被 "部分接受"(计数器被钳制在边界值),适合需要渐进式降速的场景ENX:必须启用,防止请求刷新过期时间导致窗口无限延长
监控与运维要点
部署原生速率限制器后,建议关注以下监控指标:
- 内存占用:使用
MEMORY USAGE ratelimit:*抽样检查限流 key 的内存消耗,确保未出现 key 堆积 - 限流命中率:统计
INCREX返回的增量值与请求值的差异,计算被限流请求比例 - 窗口过期延迟:监控
EXPIRED事件,确认过期 key 被及时清理 - 热点 key:对高频限流 key(如全局 API 限流)启用 Redis Cluster 的 hash-tag 分散策略
局限性说明
滑动窗口计数器方案存在固有局限:由于采用分桶聚合,窗口边界处的计数是近似值。若需要毫秒级精度的严格限流,仍需采用滑动窗口日志方案(ZADD+ZREMRANGEBYSCORE),但需承担更高的内存开销。
此外,INCREX 目前仅支持单一窗口维度。复杂场景(如 "每用户每分钟 100 请求且每小时 1000 请求" 的多层限流)仍需在应用层维护多个 key 或结合 Lua 脚本实现。
资料来源
- Redis 8.8 发布公告:https://redis.io/blog/announcing-redis-8-8/
- Hacker News Redis 8.8 讨论:https://news.ycombinator.com/
内容声明:本文无广告投放、无付费植入。
如有事实性问题,欢迎发送勘误至 i@hotdrydog.com。