消息重复消费怎么处理?让去重记录与业务结果一起提交

无敌码农编辑部 · 发布于 2026-10-29

为什么会收到同一条消息

消费者已经完成业务写入,却在发送确认前宕机,消息系统可能再次投递。网络中断、超时和人工重放也会产生重复。业务不能仅根据平时“没见过重复”就假设一次投递对应一次处理。

幂等关注同一个业务操作重复执行时,不会产生额外效果。它不等于消息系统永远只传一遍,也不一定等于响应文本完全相同。

去重键先选对

需要稳定的业务操作标识,例如支付事件 ID 或订单状态变更 ID。重试必须使用同一标识;如果每次重新生成随机 ID,唯一约束挡不住重复。不同操作也不能共用同一标识,否则会把合法事件误判成重复。

只看订单 ID 有时不够:同一订单可能发生支付、退款等不同事件。去重范围应包含事件类型或真实业务操作 ID,并与生产者契约保持一致。

业务与去重怎样原子化

当业务写入和去重记录位于同一数据库时,可以在一个事务中插入带唯一约束的消费记录,并完成业务更新;提交成功后再确认消息。如果进程在提交后确认前宕机,再次消费会命中唯一记录,避免重复修改。

不能先单独提交“已处理”,再执行业务,否则中间失败会导致消息被跳过。也不能先更新业务,再另开事务写去重记录,否则同样留有重复窗口。唯一键冲突与其他数据库错误要分别处理,不能把所有写入失败都当作“已经成功”。

外部副作用是另一条边界

发邮件、调用支付或第三方接口通常不在本地数据库事务内。优先使用对方支持的幂等键;若要可靠派发,可将待执行事件与业务一起提交,再由工作进程处理。结果不确定时需要查询或核对,不能靠本地去重表证明远端只执行一次。

去重记录保留多久取决于消息重放范围。过早删除记录后,旧消息可能再次产生效果;长期保存则增加存储成本,应有明确策略。

如何验证

在隔离环境重复投递同一事件,检查只产生一次业务结果;再分别模拟事务提交前宕机、提交后确认前宕机和远端调用超时。还要验证不同业务事件不会互相误去重。

本文没有执行具体消息队列实验。面试时按“重复来源、稳定操作 ID、事务边界、确认时机、外部副作用”回答,并说明保证只覆盖哪些资源。不要把某个队列的 exactly-once 特性直接扩展为所有业务系统端到端只执行一次。

参考资料

内容用于技术学习与求职准备,实际方案请结合版本和业务环境验证。如发现错误,欢迎通过关于页面反馈。

继续阅读