欢迎光临
我们一直在努力

RabbitMQ 重复消费问题:最通俗易懂的解决方案(幂等性)+ 实战总结

RabbitMQ 重复消费问题:最通俗易懂的解决方案(幂等性)+ 实战总结

    • 一、为什么会出现重复消费?
    • 二、核心解决思路:实现**幂等性**
    • 三、最常用、最稳定的 3 种解决方案(工作必用)
      • 方案1:唯一ID + Redis 分布式锁(生产 90% 场景用这个)
      • 方案2:数据库唯一索引(简单业务)
      • 方案3:状态机判断(订单/支付最常用)
    • 四、三种方案对比(直接背)
    • 五、最重要的 3 条规则(必须记住)
    • 六、一句话总结(面试标准答案)

🌺The Begin🌺点点关注,收藏不迷路🌺

一、为什么会出现重复消费?

RabbitMQ 无法保证消息只发一次,只能保证至少发一次。

出现重复消费的常见原因:

  • 消费者处理完业务,但没来得及 ACK,连接断了
  • 消费者异常、重启、超时
  • MQ 重试、重入队
  • 生产者重试发送
  • 结果:同一条消息被消费多次。


    二、核心解决思路:实现幂等性

    什么是幂等? 同一个消息执行多次,结果只生效一次,不会重复扣钱、重复下单。


    三、最常用、最稳定的 3 种解决方案(工作必用)

    方案1:唯一ID + Redis 分布式锁(生产 90% 场景用这个)

    原理:

    • 每条消息带一个 全局唯一ID(msgId / orderId)
    • 消费者先去 Redis 加锁
    • 加锁成功 → 消费
    • 加锁失败 → 说明已经消费过,直接跳过

    流程图:

    收到消息 → 取唯一ID → Redis加锁
    → 加锁成功:执行业务 → ACK
    → 加锁失败:直接ACK,不处理

    核心代码(简化版):

    // 1. 先拿消息唯一ID
    String msgId = message.getMessageProperties().getMessageId();

    // 2. Redis 加锁(不存在才设置)
    Boolean lock = redisTemplate.opsForValue()
    .setIfAbsent("lock:"+msgId, "1", 10, TimeUnit.MINUTES);

    if(lock == null || !lock){
    // 重复消息,直接确认,不处理
    channel.basicAck(tag, false);
    return;
    }

    // 3. 正常执行业务
    // …

    // 4. 确认消息
    channel.basicAck(tag, false);


    方案2:数据库唯一索引(简单业务)

    适用:插入数据、记录日志 给业务表的唯一字段(如 orderId)加唯一索引。

    重复插入会报 唯一键冲突,捕获后直接 ACK 即可。


    方案3:状态机判断(订单/支付最常用)

    适用:订单、支付、物流等状态流转

    比如订单状态: 1=待支付 → 2=已支付

    消费时执行:

    UPDATE order
    SET status=2
    WHERE id=? AND status=1;

    • 更新行数=0 → 已经处理过,直接跳过
    • 更新行数=1 → 正常处理

    天然幂等!


    四、三种方案对比(直接背)

    方案适用场景性能推荐度
    Redis 分布式锁 所有业务(最强通用) 极高 ⭐⭐⭐⭐⭐
    数据库唯一索引 插入类数据 良好 ⭐⭐⭐
    状态机更新 订单/支付/状态流 极高 ⭐⭐⭐⭐⭐

    五、最重要的 3 条规则(必须记住)

  • 重复消费无法避免,只能靠消费端做幂等!
  • 必须使用手动 ACK
  • 先判断幂等,再执行业务

  • 六、一句话总结(面试标准答案)

    RabbitMQ 重复消费无法避免,解决方法是实现消费端幂等。 最常用方案是 全局唯一ID + Redis 分布式锁,确保一条消息只消费一次; 订单类业务可使用 状态机更新 实现天然幂等。


    在这里插入图片描述

    🌺The End🌺点点关注,收藏不迷路🌺

    赞(0)
    未经允许不得转载:171主机测评 » RabbitMQ 重复消费问题:最通俗易懂的解决方案(幂等性)+ 实战总结
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址