redis stream 不能用 xread 替代 list 做可靠消息队列,必须配合 xadd + xreadgroup + xack 实现 ack、pending 管理和负载均衡。

Redis Stream 能完全替代 List 做消息队列,但必须用 XADD + XREADGROUP 组合,而不是 XREAD 单独用 —— 否则和 List 一样丢消息、无 ACK、无法负载均衡。
为什么不能直接用 XREAD 替代 RPOP
很多人一上来就用 XREAD STREAMS mylist $ 模拟阻塞消费,结果发现:消费者宕机后消息就没了,重连也读不到未处理的消息。这是因为 XREAD 是“单次读取 + 无状态”,它不记录谁读了哪条,也不管你有没有处理成功。
-
XREAD本质是“快照式拉取”,类似LRANGE,读完即丢上下文 - 没有消费者组,所有客户端竞争同一批消息,容易重复或遗漏
- 没有
Pending List,无法查哪些消息卡在处理中 - 不支持
XACK,也就谈不上“至少一次”投递
XADD 的 ID 必须用 *,别手写
自定义 ID 看似灵活,实际极易踩坑:XADD mystream 1-0 field val 如果流里已有 2-0,再插 1-0 会直接报错 (error) ERR The ID specified in XADD is equal or smaller than the target stream top item。生产环境几乎没人能稳控毫秒级时序和序列号。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 一律用
*让 Redis 生成1712418723456-0这类 ID,天然有序且唯一 - ID 中的
-0不是随便加的,它是同一毫秒内递增序号,1712418723456-1表示该毫秒第二条 - 如果真要控制顺序(比如按业务时间排序),得靠应用层在 value 里存时间戳字段,别动 ID
必须创建消费者组,否则等于白用 Stream
没建 XGROUP,你就只是在用一个带 ID 的 List。真正让 Stream 区别于 List 的能力——ACK、Pending、游标位移、组内负载均衡——全依赖消费者组。
- 创建时用
XGROUP CREATE mystream mygroup $,$表示只消费后续新消息;用0-0才能回溯历史 - 消费必须走
XREADGROUP GROUP mygroup consumer1 STREAMS mystream >,>是关键,它代表“从 last_delivered_id 之后读” - 每条消息处理完必须调
XACK mystream mygroup 1712418723456-0,否则XPENDING mystream mygroup里永远躺着待处理消息 - 消费者名(如
consumer1)不是占位符,不同实例要用不同名,否则XINFO CONSUMERS查不到独立状态
XTRIM 和 XDEL 别混用
XDEL 只是给消息打删除标记,不影响 XLEN 计数,也不清 Pending;XTRIM 才是真正删老数据、控流长度的正解。
- 日常运维用
XTRIM mystream MAXLEN 10000保流大小,避免内存膨胀 -
XDEL仅用于极少数需“逻辑删除某几条敏感消息”的场景,且删完要立刻XACK对应 ID,否则会卡住消费者组游标 - 执行
XTRIM后,已被XREADGROUP读过但未XACK的消息仍保留在 Pending List 中,不会丢失处理机会
Stream 的可靠性不来自“消息不删”,而来自“游标不跳、ACK 可重试、Pending 可查”。把 XGROUP 当成必选项,把 XACK 当成 if/else 里的 else 分支——漏掉任何一环,就退回 List 原点。










