当前位置: 首页 > news >正文

分布式中防止重复消费

全局唯一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+去重表或幂等逻辑解决。
http://www.xdnf.cn/news/1436923.html

相关文章:

  • Spring Security的@PreAuthorize注解为什么会知道用户角色?
  • 开悟篇Docker从零到实战一篇文章搞定
  • 基于Python毕业设计推荐:基于Django的全国降水分析可视化系统
  • 战略咨询——解读81页中小企业企业战略规划方案【附全文阅读】
  • go-mapus最简单的离线瓦片地图协作
  • C++后端开发重点知识点
  • Adafruit_nRF52_Bootloader 使用 uf2
  • Spring Cloud Config 核心原理
  • 【C++】编写通用模板代码的重要技巧:T()
  • CICD的持续集成与持续交付和Zabbix
  • 【C++】15. ⼆叉搜索树
  • 室内定位---apriltag 视觉定位demo
  • (四)Python控制结构(条件结构)
  • deepseek7b本地部署技巧,新手也能玩得转
  • 下载 | Win11 官方精简版,系统占用空间极少!(8月更新、Win 11 IoT物联网 LTSC版、适合老电脑安装使用)
  • Flink RuntimeContext和FunctionContext:状态计算的核心桥梁
  • Linux中断实验
  • 数字化转型的终极关怀:以人为本
  • Linux笔记14——shell编程基础-8
  • C#类对象映射AutoMapper
  • QT(2)
  • MTK Linux DRM分析(二十九)- MTK mtk_dsi.c(Part.1)
  • Linux 环境配置 muduo 网络库详细步骤
  • Linux 文本处理三大利器:命令小工具和sed
  • 从理念到实践:三层解耦架构与“无系统”论
  • 基于web的高校学籍管理系统的设计与实现-(源码+LW+可部署)
  • CodeBuddy 在进化:我只输入了一个地址,完成了OneCode3.0基础开发环境的配置构建
  • JWT在线解密/JWT在线解码 - 加菲工具
  • kukekey在线搭建k8sV1.30.4版本
  • 从栈中取出K个硬币的最大面值和-分组背包