分布式中防止重复消费

发布于:2025-09-03 ⋅ 阅读:(14) ⋅ 点赞:(0)

全局唯一ID(UUID、雪花算法ID等),让每条请求或消息都有一个唯一的Key——那么这个id是不是就在消息队列里面?不是在消费端的业务层生成的

好问题!这个唯一 ID 可以在不同环节生成,但通常是消息产生方(生产者)就生成好,然后随着消息一起放进消息队列传到消费者。

原因:

  • 消息的唯一性应该和消息绑定
    如果在消费端才生成唯一 ID,就失去了去重的意义,因为同一条消息多次被投递时,消费端生成的 ID 也会不同,无法判断它们是重复的。

  • 正确做法
    生产者在发送消息时生成唯一 ID,并作为消息字段传递,比如:

    {
      "msg_id": "uuid-xxxx",
      "user_id": 1001,
      "product_id": 2002,
      "timestamp": 1690000000
    }
    

    消费者拿到 msg_id 做去重。


📌 流程:

  1. 生产端生成唯一 ID(UUID/雪花算法等),写入消息内容;
  2. 将消息发送到 MQ;
  3. 消费端拿到消息 → 查询 Redis/DB 看 msg_id 是否已消费过;
  4. 如果消费过:丢弃;
  5. 如果没消费过:执行业务逻辑 → 标记已消费。

✅ 所以:
唯一 ID 是随着消息走的,通常在生产端生成并写进 MQ,而不是消费端生成。

那么防止重复消费,生产者会生成唯一ID,不管是在MQ还是在消费者,都会保存已经存储或者处理的ID,MQ接收的时候,和消费者进行处理的时候,都会先查询存储的id,看是否已经接收或者处理了?

对!你的理解很接近完整逻辑了,不过可以更精确一点:


🔍 关键点是不同环节“去重”的责任范围:

环节 是否需要存储和检查 ID 目的
生产者 一般 不检查,只负责生成唯一 ID 保证每条消息天然有唯一标识
消息队列(MQ) 看具体 MQ:
- Kafka:有 offset,不做业务级去重
- RabbitMQ:不做业务级去重
- RocketMQ:可配置去重
结论:大多数 MQ 不做业务级去重
保证消息可靠投递,但不负责业务幂等
消费者 一定要检查:消费前查 Redis/DB 看 msg_id 是否已处理过 防止重复消费、保证业务幂等性

🧩 完整流程

  1. 生产端生成唯一 ID

    {
      "msg_id": "uuid-1234-5678",
      "user_id": 1,
      "product_id": 100,
      "timestamp": 1690000000
    }
    
  2. 消息进入 MQ

    • MQ保证可靠存储和投递,但不一定去重(比如 Kafka 的 offset 是流式顺序,不去重)。
  3. 消费者拿消息 → 检查去重表

    • 查 Redis 或 DB,看 msg_id 是否存在;
  4. 不存在:执行业务逻辑,执行成功后写入去重表

  5. 存在:直接丢弃,防止重复处理


💡 总结:

  • 唯一 ID 主要是给消费者用来防重复消费的
  • MQ 大多不负责业务去重,它只保证“至少一次”投递;
  • 所以消费者必须做最终幂等性保障。

MQ 大多不负责业务去重,它只保证“至少一次”投递;—— 也就是说,MQ会存储重复消息?

对,可以这么理解:

大多数主流 MQ(Kafka、RabbitMQ、RocketMQ 等)并不做业务级去重,所以可能会“存储并投递重复消息”。
这是它们设计上的取舍。


🔍 详细解释:

  1. MQ 的设计目标

    • MQ关注的是消息的可靠性、顺序性和高吞吐,核心保证是“至少一次投递”(At-least-once)。
    • 为了确保消息不丢,生产端、Broker、消费端都可能重试,这就容易导致重复消息。
    • MQ 不做业务级去重,因为去重需要存储所有消息 ID、增加复杂度、降低性能。
  2. 为什么会产生重复消息

    • 生产者端重复发送:发送超时或网络问题,生产者以为没发成功,会重发一次;
    • Broker重复投递:消费端 ACK 超时或失败,Broker会重发;
    • 分区副本恢复:Kafka等在Leader切换时,也可能让消费端再次拉到之前的消息。
  3. 设计哲学

    • MQ通常选择简单的“至少一次”保证,让消费者来处理幂等性。
    • 因为消费者业务逻辑最了解消息是否可重复执行。

💡 结论

  • MQ可以存储重复消息,也可能重复投递;
  • 去重责任交给消费者,通过唯一ID+去重表或幂等逻辑解决。

网站公告

今日签到

点亮在社区的每一天
去签到