RocketMQ 如何保证消息不被重复消费

结论

先抛结论:保证不了

为什么会重复消费?

根本原因:ACK 机制

Consumer 消费成功了

但是 ACK 回执网络丢包

Broker 以为没消费

重新投递 → 重复消费

RocketMQ 只能保证消息「至少消费一次」(At Least Once)

不能保证「仅消费一次」

解决方案

唯一解法:消费端做幂等!

同一个消息,消费 1 次和 100 次,结果一样。

3 种最实用的幂等方案

方案 1:唯一消息 ID + 去重表(最常用、最简单)

流程:

每条消息有 唯一业务 ID(订单号、支付流水号)

消费时:

先查 DB 是否存在该 ID

存在 → 直接返回,不处理

不存在 → 执行业务,插入去重表

事务控制:业务逻辑 + 插入去重表 同一个事务

方案 2:利用数据库唯一约束(最简单)

给业务表的 业务 ID 加唯一索引

重复消息插入时

数据库直接报唯一键冲突

捕获异常,直接返回

方案 3:Redis 分布式锁(高并发)

消息 ID 作为 Redis Key

消费前:

SETNX lock:orderId

加锁成功 → 消费

加锁失败 → 重复

其实上面的方案都有问题

方案 1:唯一 ID + 去重表

高并发下绝对有风险!

数据库压力爆炸

每一条消息都要 select + insert,并发 1w+ 直接把库打垮

并发重复

两个相同请求同时 select → 都没查到 → 都执行 → 依旧重复

去重表无限膨胀

数据越来越大,查询越来越慢

方案 2:数据库唯一约束

高并发下大量异常报错

只能用于插入,不能用于更新(比如扣库存)

无法处理复杂业务

方案 3:Redis 分布式锁

锁超时风险

主从切换锁丢失

重入问题复杂

真正生成用法

不靠查询、不靠锁、不靠去重表,

靠【业务唯一 ID + 状态机 + 原子 UPDATE】!**

举2个例子

1.订单状态修改

修改状态无论多少次都是一样的结果

java 复制代码
UPDATE order
SET status = '已取消'
WHERE orderId = #{orderId}
AND status = '待取消'

2.库存扣减

库存没办法像订单表一样修改状态,只能用 去重表+扣减 来幂等

java 复制代码
@Transactional
public void consume(Message message) {

    String orderId = message.getUserProperty("orderId");
    Long goodsId = Long.valueOf(message.getUserProperty("goodsId"));

    // ==========================
    // 1. 幂等去重(唯一键)
    // ==========================
    try {
        stockUniqueLogMapper.insert(orderId);
    } catch (DuplicateKeyException e) {
        log.info("重复消费,直接ACK: {}", orderId);
        return;
    }

    // ==========================
    // 2. 扣库存
    // ==========================
    int rows = stockMapper.deductStock(goodsId);

    // ==========================
    // 3. 扣减成功 → ACK
    // ==========================
    if (rows > 0) {
        log.info("扣减成功,ACK: {}", orderId);
        return;
    }

    // ==========================
    // 4. 库存不足 → ACK + 补偿
    // ==========================
    log.error("库存不足,业务失败,ACK,发送补偿: {}", orderId);
    sendRefundMessage(orderId); 
}
相关推荐
sszdzq3 天前
docker 安装 rocketmq + dashboard
docker·容器·rocketmq
Apache RocketMQ3 天前
全新 AI 消息模型:Apache RocketMQ 如何让 AI 应用拥抱事件驱动架构?
人工智能·apache·rocketmq
better_liang4 天前
每日Java面试场景题知识点之-消息队列MQ核心场景与实战
java·面试·kafka·消息队列·rabbitmq·rocketmq·mq
超梦dasgg4 天前
Java 生产环境 RocketMQ 架构与部署指南
java·rocketmq·java-rocketmq
jameslogo5 天前
MQ消息队列常见问题
rocketmq
阿里云云原生5 天前
Apache RocketMQ 5.5.0 发布:LiteTopic 深度解析,如何支撑百万级 AI 会话并发?
apache·rocketmq
孫治AllenSun5 天前
【RocketMQ】windows安装
windows·rocketmq
jameslogo8 天前
RocketMQ与Kafka零拷贝机制
分布式·kafka·rocketmq
IT界的老黄牛9 天前
从 MQ 积压追到事件总线:诊断 4K 线程吃光 7G 内存的实战
java·运维·rocketmq
电魂泡哥9 天前
RocketMQ Dledger 集群与 Raft 协议
java·rocketmq·java-rocketmq