3.如何保证消息不被重复消费?

一、问题引导思维

  • 使用 MQ(消息队列)时,是否考虑过消费端可能会 重复处理一条消息
  • 如何判断一条消息是否已经消费过?
  • 如果重复消费不可避免,该如何设计业务逻辑保证 幂等性

面试官心理:你不仅要知道 MQ 会出现重复消费的场景,更要知道如何设计“幂等的消费逻辑”,这体现你对架构鲁棒性的理解和实践能力。


二、为什么会重复消费?(通俗解释)

消息队列在实际使用过程中重复消费是常态,不是异常。

以 Kafka 为例:

  1. 消费者从 Kafka 拿到一条消息,先处理再提交 offset。
  2. 如果处理完成后还没来得及提交 offset 就宕机了,那么offset 没更新,下一次启动还是会从旧 offset 开始读,之前处理过的消息就会再来一次

生活类比:

  • 你吃饭前记得拍个照(提交 offset),结果照片没拍就断电了,朋友再让你拍时,只能从上一顿饭重来,饭你是吃过了,但别人不知道。

三、如何解决重复消费问题?(思路 + 实践)

重复消费不可怕,关键是保证幂等性 —— 相同的消息消费多次不会影响最终结果。

常见解决方案:

场景 解决方案 幂等性实现思路
数据库写入操作 使用唯一索引或幂等判断 主键或唯一字段控制重复插入
Redis 写数据 使用 SETSETNX 命令 SET 覆盖,SETNX 只设置一次
有业务 ID(如订单号) 消息体中携带唯一 msgIdorderId 消费前查 Redis 或 DB 判断是否处理过
HTTP/接口幂等 提供 token 或请求唯一标识,避免重复提交 中间件记录标识处理状态
状态机类操作(如扣库存) 记录“幂等日志”或“操作流水”,控制状态变更幂等 判断之前状态是否已变更

四、为什么 MQ 本身不负责幂等性?

因为 MQ 的设计原则是:

  • 尽最大努力送达消息(不丢)
  • 允许少量重复投递
  • 幂等性是 业务层 的责任,每个业务场景都不一样,MQ 没法一刀切处理

五、不同 MQ 的重复消费特点

MQ 类型 是否可能重复投递 原因说明
Kafka offset 提交机制存在延迟,处理成功但 offset 未提交
RabbitMQ 使用 at-least-once 模式,可能重新投递
RocketMQ 支持重试机制,失败后会再次投递消息

六、选型建议(面对重复消息的处理)

场景 建议
高性能系统,写入频繁 Redis 结构天然幂等,适合做幂等去重记录
强一致性需求,事务敏感 数据库唯一索引 + 幂等判断逻辑
分布式订单、库存系统 携带业务唯一 ID,并结合幂等日志或流水进行判断处理
批量消费消息 持久化消费日志,定期清理已处理 ID
多实例并发消费 Redis + 分布式锁控制并发幂等消费

七、一句话总结

重复消费是消息队列的正常现象,关键在于你有没有设计幂等逻辑,确保系统的数据不会因为重复处理而“翻车”。

posted @ 2025-06-17 15:33  只待时光静好  阅读(151)  评论(0)    收藏  举报