Redis Stream 消费组怎样使用 NOACK
来源:17golang原创
时间:2026-10-15 00:54:23 498浏览 收藏
Redis Stream 消费组在 XREADGROUP 中加上 NOACK,可以让本次读取的新消息不进入 Pending Entries List(PEL)。典型写法是 XREADGROUP GROUP 组名 消费者名 COUNT 100 NOACK STREAMS 流名 >。代价是:它把读取视作已确认,不等业务处理完成;消费者随后失败时,这些消息没有该组的 Pending 记录可供接管。
如果只是允许偶发缺失的实时展示,我会考虑它;如果是必须补偿的订单处理,我会保留普通消费与成功后的 XACK。这里用一个假设的实时展示任务解释选择,不声称亲历了故障或完成了压测。
官方命令地址:https://redis.io/docs/latest/commands/xreadgroup/
我会先把处理成功单独计数
假设有一个活动看板,把消息交给多个消费者做短时展示。旧消息很快失去展示价值,但是否允许丢失仍然需要业务明确决定。我不会先问“能快多少”,而会先区分三个数量:消费端收到多少条、业务真正完成多少条、组内还有多少条 Pending。
NOACK 最容易造成的误判,是 Pending 变成零后就认为任务全部成功。对我而言,零 Pending 只说明没有相应待确认记录,不等于每条消息的业务动作已完成。测量基线应保留业务成功计数,而不是只保留 Redis 返回数量。
| 计数 | 观察对象 | 不能代替什么 |
|---|---|---|
| 读取条数 | 客户端实际收到的消息 | 业务完成条数 |
| 成功条数 | 业务动作完成的消息 ID | 全部曾读取的消息 |
| Pending 条数 | PEL 中的待确认记录 | NOACK 模式下的失败条数 |
NOACK 省下的是待确认记录
普通消费组会跟踪已投递但未确认的消息,业务成功后用 XACK 移除相应 PEL 记录。NOACK 则跳过新消息的这份待确认记录。因此它改变的是确认与恢复约定,不是把业务处理变成 Redis 内部的原子操作。
在这个区别里,我会把 Stream 消息本体、PEL 与业务处理结果分开看。NOACK 不是删除 Stream 条目的命令;即便消息仍在 Stream 中,也不代表业务知道哪些已成功、哪些需要重做。保留的条目可以按 ID 范围查询,另做重放则需要自己定义范围、完成记录和去重规则,不能当作 Pending 接管的等价替代。
有关确认职责可参照官方 XACK 说明:https://redis.io/docs/latest/commands/xack/;原始条目的范围查询见:https://redis.io/docs/latest/commands/xrange/。

把 NOACK 放到新消息读取中
下面是 shell 中的命令片段,假设流与组已经存在。注意把 > 引起来,避免 shell 将它当作输出重定向。
# 读取该组尚未投递的新消息,最多等待 1000 毫秒;不建立新消息的 PEL 记录 redis-cli XREADGROUP GROUP display_group worker_a COUNT 100 BLOCK 1000 NOACK STREAMS display_events '>'
GROUP 后面依次是组名和消费者名;组内不同工作实例应使用不同消费者名。COUNT 是每条流的数量上限,并不是必须凑满的批量大小。BLOCK 控制无可读消息时的等待,1000 的单位是毫秒,不是业务处理超时。
这里没有使用 CLAIM 扩展,> 表示读取该组尚未投递的新消息。若改用 0 或其他具体 ID,含义变成读取当前消费者已有的 Pending;此时 NOACK 与 BLOCK 被忽略。不要靠 NOACK STREAMS 流名 0 清空历史待确认记录。以上范围以官方 XREADGROUP 规则为准。
用两个组比较同一批消息
下面的示例供读者在独立测试实例执行,未在本机运行。前提是 demo:noack:events 这个键此前不存在,没有其他生产者、消费者或裁剪操作;两个组使用相同起点,从而能分别消费同样的两条消息。固定 ID 只用于教学,不表示真实生成时间。
# 创建两个独立组;起点 0 允许读取从头保留的消息,MKSTREAM 创建空流 redis-cli XGROUP CREATE demo:noack:events fast_group 0 MKSTREAM redis-cli XGROUP CREATE demo:noack:events tracked_group 0 # 在新建的空流中写入两条示例事件,固定 ID 便于后续引用 redis-cli XADD demo:noack:events 1-0 event_key screen_a value 17 redis-cli XADD demo:noack:events 2-0 event_key screen_b value 29 # 同一批消息由不同组分别读取;fast_group 不建立 Pending redis-cli XREADGROUP GROUP fast_group fast_a COUNT 2 NOACK STREAMS demo:noack:events '>' redis-cli XREADGROUP GROUP tracked_group tracked_a COUNT 2 STREAMS demo:noack:events '>' # 比较各组 Pending,并单独查看仍保留的消息本体 redis-cli XPENDING demo:noack:events fast_group redis-cli XPENDING demo:noack:events tracked_group redis-cli XRANGE demo:noack:events - + COUNT 2 # 仅当对应业务处理成功时,普通消费组才确认这些 ID redis-cli XACK demo:noack:events tracked_group 1-0 2-0
按命令语义预期,两个读取都可得到两条事件;fast_group 的 Pending 数量为 0,tracked_group 在确认前为 2。两条原始消息仍可通过 XRANGE 查询;最后的 XACK 移除普通组的待确认记录。这些是基于前提的预期,不是实测输出,也不是吞吐基准。
普通组最后的 XACK 演示确认动作的位置,不能照抄到业务失败分支。示例对象留在测试实例中,不自动清理其他键。若创建时返回 BUSYGROUP,说明组已存在,应停止并换未使用的测试对象,不能继续拿旧组状态作对照。
组起点与创建行为参照:https://redis.io/docs/latest/commands/xgroup-create/;Pending 查询参照:https://redis.io/docs/latest/commands/xpending/。
性能收益要和缺失代价一起量
少维护一份记录、少一次确认动作,可能减少开销,但我不会由此直接写出“性能提升一倍”。普通消费也可以批量 XACK,网络、业务耗时和批量大小都可能掩盖差异。真正的对比需要相同消息内容、消费者数量、并发、网络条件与持久化设置。
如果由我安排实验,会使用两轮隔离负载:一轮普通读取并在业务成功后批量确认,一轮 NOACK。每轮固定消息数量,记录相同观测窗口;除了读取速率,还要记录业务成功速率、端到端 P95 延迟、客户端和 Redis CPU,以及按实际成功 ID 计算的缺失情况。
| 指标 | 普通读取+确认 | NOACK |
|---|---|---|
| 业务成功条数/秒 | 需实测 | 需实测 |
| 端到端 P95 延迟 | 需实测 | 需实测 |
| CPU 与资源开销 | 需实测 | 需实测 |
| 读取后进程退出的未完成 ID | 观察 Pending 与恢复结果 | 观察业务缺失与外部补偿 |
故障实验也只应在测试环境进行:让消费者读取后、业务完成前退出,再比较未完成消息的可定位性。普通组虽然保留 Pending,仍要实现接管、重试和业务幂等;NOACK 则没有这些新消息的 PEL 依据。我的判断会同时看速度变化和缺失是否符合约定,而不是只挑吞吐好看的数字。

需要补偿时我会保留确认机制
对临时展示、允许明确范围内缺失的非关键数据,NOACK 可以是一个简化选择。但如果处理失败必须知道“哪一条没完成”,我会保留 PEL 与显式确认。业务要求不能因为 Pending 看起来更干净而被悄悄改变。
切换选项还要记住:NOACK 是本次读取的选择,不是永久关闭消费组的确认能力。之前积累的 Pending 不会被它自动清掉;后续不带 NOACK 的新消息仍按普通确认约定处理。混用前必须让监控能识别哪些消息采用了哪种处理策略。
常见问题
NOACK 的消息还需要 XACK 吗?对于该方式新读取且未加入 PEL 的消息,不需要用 XACK 移除不存在的记录;业务成功仍应自行记录。
Pending 为零能说明没有失败吗?不能。尤其在 NOACK 下,它没有收集这批消息的业务失败情况。
消息仍在 Stream 里,为什么不能直接补偿?原始事件不等于完成状态。重放前还需确定缺失范围与去重约定,不能把所有历史事件无差别重复处理。
我的取舍是:先明确可容忍的缺失,再衡量省下的开销。NOACK 解决的是“不需要维护待确认记录”,不是“可靠消费无需确认”。
-
191 收藏
-
454 收藏
-
108 收藏
-
332 收藏
-
291 收藏
-
269 收藏
-
138 收藏
-
495 收藏
-
379 收藏
-
326 收藏
-
110 收藏
-
187 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习