Redis 8.8 INCREX 怎么做窗口限流:计数、上限和过期一次核对
做登录接口每分钟100次的限流时,常规的老写法一般是先给Redis做计数累加,再单独给键设置过期时间。这两个操作中间一旦出现异常断开,或者刚好插进来并发请求,限流窗口就可能丢失TTL,后续的请求根本没法确认自己有没有正确拿到配额。Redis 8.8 的 INCREX 直接把计数校验、配额上限判断和过期时间设置整合成一个原子操作,刚好补上了这段很容易出bug的空隙。
INCREX从 Redis 开源版 8.8.0 开始正式提供,时间复杂度为 O(1)。UBOUND 100遇到第101次超限请求时会返回实际增量0,默认不会让计数突破预先设置的上限。EX 60 ENX只会在当前新窗口没有TTL的时候设置60秒过期,后续进来的请求不会反复重置续期时间。- 旧版本的Redis不识别这条命令,要提前保留Lua脚本或者“计数+过期原子封装”的兼容兜底路径。
Redis 8.8 为什么新增 INCREX
传统的固定窗口限流逻辑至少要走 INCR 和 EXPIRE 两个步骤。第一次请求创建计数键,第二步再设置窗口过期;如果客户端两步之间直接断开,或者多个客户端同时判断“这是个新键”,就会出现键没有过期时间、限流窗口被无限制拉长等问题。把这段逻辑塞进Lua脚本可以保证原子性,但每个项目都得单独维护脚本、参数传递规则和返回值约定,额外的维护成本不低。
INCREX 是Redis 8.8新增的原生命令,除了常规的累加增量之外,还支持直接设置 EX、PX、EXAT 或者 PXAT,在同一次请求操作里就能给计数加上上下边界限制。它会返回两个值:更新后的新计数、实际生效的增量,返回结果可以直接拿来判断“放行还是拦截”,不用再多做额外判断。

60秒窗口限流的最小写法
下面的示例把用户ID拼进限流键名,实现每个用户60秒内最多放行100次的规则。先清理测试用的旧键,再分别观察首次请求、计数满额之后的返回结果。
redis-cli DEL ratelimit:user-42 redis-cli INCREX ratelimit:user-42 BYINT 1 UBOUND 100 EX 60 ENX redis-cli TTL ratelimit:user-42 redis-cli INCREX ratelimit:user-42 BYINT 1 UBOUND 100 EX 60 ENX
第一次调用会创建全新的计数键,典型返回是 1 和 1;键的剩余TTL会非常接近60秒。同一个窗口内再次调用,计数会正常累加但TTL不会被重置回60秒。等到计数涨到100之后,下一次调用会直接返回当前计数值和 0,应用层拿到这个结果就可以直接返回HTTP 429限流响应。
1) (integer) 1 2) (integer) 1 1) (integer) 60 2) (integer) 0
这里的 ENX 参数非常关键:它要求只有在键当前不存在过期时间的时候才写入TTL。要是漏掉这个参数,每次请求都可能重新覆盖写入窗口过期时间,原本的固定窗口就会变成“最后一次请求结束后再等60秒”的类滑动窗口效果,完全不符合固定限流的预期。
UBOUND、SATURATE 和实际增量怎么选
不带 SATURATE 参数的时候,请求越过 UBOUND 上限的操作会被直接拒绝,键的计数值和原有TTL都保持不变,返回的实际增量是0。这是限流场景最常用的行为,上层调用方只需要判断第二个返回值是不是大于0就能知道有没有拿到配额。
如果业务侧希望计数值最多停在设定的上限,不要把越界的操作直接当成失败,就可以加上 SATURATE:
redis-cli SET quota:team-a 99 redis-cli INCREX quota:team-a BYINT 5 UBOUND 100 SATURATE redis-cli GET quota:team-a
执行结果会直接把值封顶到100,返回的实际增量是1。这个选项更适合“累计消耗不超过总预算”这类场景,不能直接拿来判断请求有没有拿到本次的限流名额;常规限流场景还是建议保留默认的越界拒绝语义更稳妥。
| 选项 | 用途 | 边界行为 |
|---|---|---|
UBOUND | 设置计数值上限 | 越过边界默认不改写键值,实际增量返回0 |
LBOUND | 设置计数值下限 | 适合带负增量调整的余额或者配额扣减场景 |
SATURATE | 对数值执行封顶或封底 | 返回截断之后实际生效的增量 |
ENX | 只给新建窗口设置TTL | 键已经有TTL的时候不会做续期操作 |
从 INCR 加 EXPIRE 迁移时要留意什么
第一个要过的门槛是服务端版本校验。INCREX 从Redis开源版8.8.0开始提供,正式升级之前可以先执行下面的命令确认目标实例能不能正常识别这条命令:
redis-cli COMMAND INFO INCREX redis-cli INFO server | grep redis_version
生产环境如果还有7.x或者更早版本的实例,不要直接把新命令全量下发到所有节点。可以先在客户端侧按实例版本做分支选择:8.8版本走 INCREX ... UBOUND ... EX ... ENX 新命令,旧版本继续用之前已经跑稳了的Lua原子脚本。灰度观察阶段重点关注限流命中率、429响应数量、限流键的TTL分布,以及有没有出现“命令不存在”的报错。
第二个要注意的门槛是客户端返回值解析。很多官方客户端暂时没有给这条新命令封装专属方法,需要用通用自定义命令的接口调用,再把返回的数组拆成两个单独的数值字段。不要只取返回的第一个计数字段做判断,不然计数满100之后很容易把“当前值是100”误判成“本次请求成功拿到配额”。

上线前的四个检查点
- 键名要带上稳定的业务限流维度,比如
ratelimit:user-42,不要把所有用户的请求都统计到同一个公共键里。 - 窗口时长要和业务对外公示的规则对齐,
EX 60是从第一次请求进入时开始计时,不是按自然整分钟的起点对齐。 - 逻辑里要明确判断只有第二个返回值大于0的时候才放行,返回0的时候直接映射成明确的限流响应。
- Redis集群版本和所用客户端都要确认支持8.8新命令,仍在运行旧版本的节点要提前准备好可回退的兼容路径。
常见问题
INCREX 和 INCRBY、EXPIRE 有什么区别?
INCRBY 只负责修改计数值,EXPIRE 单独负责操作TTL,两个命令组合使用需要额外做原子性封装;INCREX 在一次原生命令里就能完成计数累加、边界判断和过期设置三个动作。
为什么窗口限流要加 ENX?
没有加 ENX 参数的话,每次请求都可能重新写入过期时间,导致限流窗口不断向后移动。ENX 可以保证TTL只有在窗口第一次创建的时候才会被写入。
返回的第二个数字为 0 代表什么?
一般代表请求越过了预先设置的上下边界,计数值没有被实际修改。固定窗口限流的场景下,这个返回值就是“本次没有拿到可用配额”的直接信号。
Redis 7 能使用 INCREX 吗?
不能直接使用。Redis 7 要继续用已经验证过的兼容实现,在客户端侧提前根据服务端版本或者能力探测的结果自动选择对应执行路径即可。
最后的落地判断
如果线上服务已经在运行Redis 8.8,做固定窗口限流可以优先用 INCREX + UBOUND + EX + ENX 这组最小参数组合:单命令原子操作、O(1)复杂度、直接返回实际增量,代码里的放行判断逻辑也会更清晰简洁。要是环境还跑在旧版本上,先保留之前已经验证稳定的原子脚本,等灰度确认好客户端返回解析逻辑和核心监控指标之后再逐步切换就行。
PHP Webhook 如何防重放攻击:签名时间窗、nonce 与幂等记录的完整流程
- 上一篇
- PHP Webhook 如何防重放攻击:签名时间窗、nonce 与幂等记录的完整流程
- 下一篇
- MySQL JSON 字段怎么给列表筛选提速:生成列、索引与 NULL 边界
-
- 数据库 · Redis | 3小时前 | Redis · lua · eval · eval Redis Lua 多 key 原子校验
- Redis Lua 脚本读取多个 key 时怎么保持原子校验
- 423浏览 收藏
-
- 数据库 · Redis | 11小时前 |
- Redis ZRANGEBYSCORE 分数边界怎么写成开区间
- 440浏览 收藏
-
- 数据库 · Redis | 18小时前 |
- Redis EXPIRE 续期时为什么会把旧过期时间覆盖
- 273浏览 收藏
-
- 数据库 · Redis | 22小时前 | Redis · 消息队列 · Stream · XTRIM · maxlen Redis Stream XTRIM
- Redis Stream 裁剪后为什么还会保留部分消息
- 441浏览 收藏
-
- 数据库 · Redis | 23小时前 | Redis · 消息队列 · Stream · XREADGROUP · Redis Stream XREADGROUP
- Redis XREADGROUP 没有新消息时怎么设置阻塞时间
- 469浏览 收藏
-
- 数据库 · Redis | 1天前 | Redis · Stream · 消费组 · Redis Stream XPENDING Pending Entries List
- Redis XPENDING 怎么查看消费组中最老的未确认消息
- 259浏览 收藏
-
- 数据库 · Redis | 1天前 |
- Redis Lua 脚本返回数组时客户端为什么出现 nil
- 448浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 61次使用
-
- SuperCLUE
- SuperCLUE是权威的中文大语言模型综合评测基准,涵盖语言理解、知识应用、AI Agent智能体及安全性等12项核心能力。通过多轮对话与客观测试,定期发布榜单与技术报告,为模型研发、优化及行业选型提供科学依据。
- 213次使用
-
- C-Eval
- 深入了解C-Eval中文评估套件,涵盖52个学科与4级难度。本文详解其功能特点、Zero-shot/Few-shot使用方法及代码示例,助您全面评测LLM中文理解与泛化能力。
- 144次使用
-
- AI Prompt Library
- 探索AI Prompt Library免费资源库,涵盖营销、写作及多场景AI提示词。兼容ChatGPT、Claude等工具,一键复制优化输出,提升工作效率。
- 77次使用
-
- Generrated
- Generrated汇集9300+张DALL·E生成图像及对应提示词,支持查看完整图集、对比DALL·E 2与3版本差异,是AI绘图新手学习Prompt设计与获取创作灵感的实用工具。
- 55次使用
-
- 接口返回的数据和数据库不一致怎么办?按数据生命周期排查
- 2026-06-27 398浏览
-
- Go与Redis实现分布式互斥锁和红锁
- 2022-12-22 117浏览
-
- Go+Redis实现延迟队列实操
- 2023-02-23 426浏览
-
- 关于golangtest缓存问题
- 2023-01-01 298浏览
-
- Go语言框架快速集成限流中间件详解
- 2022-12-23 290浏览

