一、哪些场景会丢失消息

业务场景:下单支付成功后、给用户发送消费券

业务步骤:

image

用户反馈:支付成功以后,没有收到优惠券。

原因:支付成功的消息丢失了

二、可能丢失消息的环节:

1、订单系统(生产者)向MQ推送消息的过程中丢失

可能原因:1、发送MQ的过程中网络波动;2、MQ确实收到消息了,但MQ的网络通信出现问题导致消息无法完成处理;3、在发送消息给MQ的过程中,MQ主节点故障,恰好在主从切换;4、其他等等原因

2、消息已经发送到MQ,MQ自身导致消息的丢失

可能原因:rocketMQ真正写入成功是指消息写入到了PageCache中,如果在这个过程中服务宕机就会导致消息写入失败

3、MQ已经写入到磁盘,但磁盘损坏导致消息的丢失

4、消息已经发送到优惠券系统(消费者)

可能原因:消费者已经接受消息,并完成了ACK,但这时候消费者在发优惠券前宕机了,也会导致丢失

三、生产者消息零丢失方案

问题:订单已经变更支付状态,但是消息却没有发送到MQ

解决方案

1、发送消息,但消息状态是Half,判断网络是否正常;如果成功发送,更新订单为已支付,如果失败,就退款。

image

2、检查本地事务是否成功,如果订单状态更新成功,就向MQ发送commit,本地事务提交;如果失败就rollback,本地事务也跟着回滚。

MQ接受到commit后会把之间的half消息进行提交,提交后,消息就对优惠券系统(消费者)可见,此时优惠券系统就可以消费消息。

image

如果在订单更新状态后发送commit失败,这时候会有一个定时器,处理长时间未提交或回滚的half消息,会回查订单系统的事务状态。

回查事务状态:如果事务时已提交,那么订单系统就发送commit,如果是已回滚,那么久发送rollback。

image

补偿机制:

image

这个机制确保了订单发送消息给MQ的过程的一个一致性。

总结:事务消息机制或重试机制

三、事务消息机制的底层原理

问题:half消息是如何对消费者不可见的?

image

如果这时候消息回滚,half消息并不会物理删除,而是逻辑删除

image

如果没有收到commit/rollback,则会触发MQ的补偿机制

1、定时任务扫描RMQ_SYS_TRANS_HALF_TOPIC中长时间没有提交/回滚的消息,回查生产者业务的本地事务状态。

2、最多重试15次,超过15次,自动rollback。

事务消息流程机制

image

除了事务消息,是否还有其他方法可以保证消息零丢失?

1、同步发送+重试:把消息发送也放在本地事务中,这时候修改订单操作+发送消息要么都执行、要么都不执行。

缺点:这个发送消息可能会因为网络堵塞而不断重试导致耗时过长,支付超时。

四、Broker消息零丢失方案

问题:消息还在PageCache,还没刷盘到磁盘,如果此时服务宕机,数据就会丢失。

image

解决方案:

MQ有2种刷盘机制:异步刷盘和同步刷盘。

异步刷盘:只把数据刷到PageCache,吞吐量和性能高,但容易丢失数据

同步刷盘:把数据刷到磁盘中才算完成,性能低,但安全

因此,在这里我们可以采用同步刷盘的策略来避免消息的丢失。

问题:这时候磁盘坏了呢?

这时候即使同步刷盘,数据还是会丢失。

解决方案:主从同步避免单机的磁盘损坏风险。

总结:要保证不丢失,要同步刷盘+主从同步

五、消费者消息零丢失方案

问题:消费者在拿到消息以后,还没来的及处理,就提交offset给broker,但这时,消费者服务器挂了。

image

解决方案:把自动提交改为手动提交的方式。

问题:现在已经手动提交了,但是却开启了子线程异步消费消息,这时候子线程消费完消息手动提交,但是主线程的业务还没走完,然后又宕机了,这时候重启后因为offset已经被子线程提交了,所以消费者就不会再去拉去消息了。

总结:要保证不丢失,需要改成手动提交+同步消费。

最后,总结:

生产者消息零丢失方案:事务消息或重试机制

broker消息零丢失方案:同步刷盘+主从架构

消费者消息零丢失方案:手动提交+同步消费

学习视频来源:https://www.bilibili.com/video/BV1BB4y1H7VW?share_source=copy_web

最后修改:2026 年 06 月 05 日
如果觉得我的文章对你有用,请随意赞赏