当前位置: 首页 > 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.dtcms.com/a/363416.html

相关文章:

  • 计算机视觉与深度学习 | 视觉里程计技术全解析:定义、原理、与SLAM的关系及应用场景
  • STM32之SPI详解
  • Linux《进程信号(上)》
  • mit6.031 2023spring 软件构造 笔记 Specification
  • 【XR硬件系列】Apple Vision Pro 完全解读:苹果为我们定义了怎样的 “空间计算” 未来?
  • springboot项目使用websocket功能,使用了nginx反向代理后连接失败问题解决
  • 集采与反腐双重压力下,医药销售的破局之道:从资源依赖到价值重构
  • DASK shuffle任务图分析
  • 阅读Linux 4.0内核RMAP机制的代码,画出父子进程之间VMA、AVC、anon_vma和page等数据结构之间的关系图。
  • 解密llama.cpp CUDA后端:512 token大模型批处理的异步流水线架构
  • 【llama.cpp】qwen2_vl_surgery.py详解
  • 应用层:HTTP/HTTPS协议
  • 2025年- H109-Lc1493. 删掉一个元素以后全为 1 的最长子数组(双指针)--Java版
  • 软件测试小结(1)
  • 【完整源码+数据集+部署教程】粘土石实例分割系统源码和数据集:改进yolo11-LVMB
  • MVP架构深层剖析-从六大设计原则的实现角度到用依赖注入深度解耦
  • 安全芯片助力游戏设备防抄板
  • 「Windows自动化之王:PowerShell极简美学」​
  • 微信小程序 navigateTo 栈超过多层后会失效
  • 【开题答辩全过程】以 基于微信小程序的体育场馆预约管理系统为例,包含答辩的问题和答案
  • 【Git】一文详解Git Rebase和Merge区别 看完必会
  • 整体认识K8s之PriorityClass优先级调度/HPA自动扩缩容机制
  • golang 依赖管理
  • 网络技术名词 CDN NAT GA DNS
  • 深度学习篇---Pytorch常用优化器
  • 力扣72:编辑距离
  • 用 PyTorch 实现食品图像分类:从数据预处理到模型训练与预测
  • mayfly-go:web 版 linux、数据库等管理平台
  • 码农必备!本地调试神器act,GitHub Actions最佳拍档
  • C++ 条件变量,互斥锁