返回首页
Redis

Redis Streams 可靠消费:Pending、ACK 与重复处理

Redis Streams 很适合在中小型系统中承载异步事件。它比简单 Pub/Sub 多了持久化、消费组、Pending 和 ACK,但“使用了 Streams”并不等于消息天然可靠。

一条消息的生命周期

生产者使用 XADD 写入 Stream。消费者组中的消费者通过 XREADGROUP 获取消息后,这条消息进入 Pending Entries List。业务处理成功后执行 XACK,消息才会从该组的 Pending 状态移除。

XADD → XREADGROUP → Pending → 业务处理 → XACK

如果先 ACK 再处理,进程崩溃可能导致消息丢失;如果处理成功但 ACK 前崩溃,消息会再次被消费。因此常见实现提供的是“至少一次”,业务必须面对重复。

怎样恢复 Pending 消息

消费者需要定期检查长时间未确认的消息,并把已经超时的任务重新分配给存活消费者。恢复逻辑还要设置重试上限和死信处理,否则一条永久失败的消息会无限循环。

仅消费新消息而不处理 Pending,是很隐蔽的可靠性缺陷:正常演示可以运行,但消费者重启后历史失败事件永远无人处理。

幂等比“恰好一次”更现实

网络超时使消费者无法确定操作是否已经成功。与其宣称端到端“恰好一次”,更可靠的做法是让业务处理可重复执行,例如:

  • 为事件设置唯一 ID,并记录已处理事件;
  • 使用数据库唯一约束防止重复插入;
  • 统计类任务按批次聚合并执行原子增量;
  • 把业务写入和消费记录放入同一事务边界。

在短链接统计中的应用

重定向请求只负责写入访问事件,消费者异步更新 PV。这样缩短了跳转延迟,但必须监控 Stream 长度、Pending 数量、最老 Pending 时长、消费延迟与失败次数。

异步系统的可靠性不是某个 API 提供的,而是由投递语义、ACK 时机、重试、幂等和监控共同组成。