网站优化

网站优化

Products

当前位置:首页 > 网站优化 >

如何让消息队列在消费时实现真正的幂等性呢?

GG网络技术分享 2026-04-16 17:27 0


哎,说起消息队列的幂等性啊,那真是个让人头疼的问题!当初我刚接手项目的时候,就主要原因是没考虑到这块儿,导致线上数据混乱了好一阵子。 小丑竟是我自己。 用户反馈订单重复创建,财务那边天天找我核对账单… 那滋味儿,简直生不如死!所以今天就来好好唠唠这个话题,争取把这块儿讲透彻了。

什么是消息队列的幂等性?

简单就是你一条消息,无论被消费者处理多少次后来啊都应该是一样的。就好像你给银行转账100块钱,不管这个转账请求发几次你的账户到头来扣掉的钱都应该是100块。不能主要原因是网络抖动或者消费者BUG导致钱被扣了两次、三次… 那可就闹大了!

消息队列如何保证消费的幂等性

基本上... 消息队列中如何保证幂等性.可以看出,消息发送和消息消费两个步骤是有可能产生消息不幂等的问题。.步骤4:消息队列服务 MQ-Server 将消息发给给消费端 MQ-Client Consumer.

为什么需要考虑幂等性?

想想看, 如果你的系统没有做好幂等性处理,那么在以下几种情况下就很容易出问题:

  • 网络重试: 客户端发送请求后由于网络问题收不到响应,客户端可能会重试。
  • MQ自身重试: 消息队列为了保证可靠性,可能会对失败的消息进行重试。
  • 消费者BUG: 消费者在处理消息的过程中出现异常导致处理失败。

这些情况都会导致同一条消息被多次处理,从而造成数据的不一致。

最朴素的方法:先判断后更新

一开始我也想过最简单的办法——先判断状态再更新。比如对于一个订单创建的请求,先查询一下这个用户有没有已经创建过订单了。如果没有的话就创建订单;如果有的话就直接返回成功。但是!这种方法在高并发场景下很容易出问题,太治愈了。。

以处理用户消费订单业务为例, 如果用户在同一时间发起了多次创建订单请求,那么这时多个请求发送过来很简单,我们可以先判断当前用户订单状态是否已经更新如果已经更新了那么我们就没必要再进行更新了,但是这里 记住... 会有一个问题,如果订单处理业务要耗费很长时间,在并发场景下多个请求一边打到服务器上,就有可能出现其实当前正在更新状态,但是其实吧判断并不生效的后来啊,所以呢这种最简朴的方法在业务繁琐的场景下并不奏效

加锁解决?高并发下效率太低!

然后我就想到了加锁。在处理业务之前先获取一把锁,这样就能保证只有一个线程能施行业务逻辑了。 体验感拉满。 但是加锁也会带来问题——性能下降!在高并发的场景下加锁会导致大量的线程阻塞等待锁释放。

人间清醒。 就拿锁的两个分支乐观锁与悲观锁举例,如果我们使用悲观锁,在第一个请求来的时候就直接锁住整个业务,那么当其他请求过来时,就必须要等待,这样降低了其他服务施行的性能,得不偿失;而如果我们使用乐观锁,在第一个请求过来时,每次都施行,为了保证业务只能施行一次,我们必须要加一些版本号的字段等保证这个业务不会被重复施行,这样做加大了代码的复杂度

解决方案 优点 缺点
悲观锁 简单易懂 并发性能差
乐观锁 并发性能相对较好 代码复杂度高
数据库事务+插入唯一记录表 可以有效防止重复操作 增加了数据库压力

事务 + 插入唯一记录表

为了解决上述问题呢? 我尝试了一种更复杂的方案——数据库事务+插入唯一记录表. 我们额外建立一张数据库表为消息表,当我们施行业务代码时按照如下流程施行:

  • 先说说尝试向该表中插入一条记录。
  • 如果在事务中插入成功说明之前没有被处理过可以进行后续操作.
  • 如果插入失败说明已经被其他线程/进程处理过了直接返回成功.

具体流程如下

  1. 开始事务
  2. 尝试插入唯一记录
  3. 如果插入成功继续施行你的核心业务逻辑
  4. 提交事务
  5. 如果插入失败直接回滚事务并返回成功

延信队列 + 重试机制

常用MQ产品对比
产品名称特点适用场景
Kafka高吞吐量、 分布式、持久化日志收集、流式计算、事件驱动架构
RabbitMQ灵活、可靠、支持多种协议企业级应用、异步任务处理、复杂路由需求
RocketMQ高性能、低延迟、金融级可靠性电商交易系统、金融支付系统、实时风控系统

状态字段 + Redis

带过期时间的消费幂等性策略

##:还是得靠你自己!

说了这么多方法吧... 但其实我要表达的是:**消息队列本身是无法保 又爱又恨。 证消费幂等的**! 它只能尽力保证消息至少到达一次或者最多到达一次。

总之啊!要实现真正的幂等性还得靠你自己去设计合适的方案啦~希望我的分享能给你带来一些启发和帮助!祝你好运!!!


提交需求或反馈

Demand feedback