Skip to content

如何保证消息不丢失

消息丢失场景

数据丢失在MQ中比较常见,一般丢失数据都是在跨网络的部分,比如1、2、4。

  • 生产者发数据
  • 消费者消费数据
  • MQ内部主从同步

而MQ写数据到磁盘过程也是有丢失数据的可能的。

一般写数据到磁盘不会直接去写,而是利用操作系统的缓存,先写数据到缓存中,等待操作系统异步刷进磁盘。

比如 Prometheus 的 WAL 机制。

image.png

事务消息-生产者

使用事务消息能保证本地事务和写入MQ的事务一致性。

比如订单场景,只保证本地下订单和向MQ发消息的事务一致性。不会像MySQL一样保证数据库事务。

只是保证了业务的分布式一致性问题。

事务消息能保证生产者发送数据到MQ过程不会丢数据。也就是第1个问题。

异步消费-消费者

一般消费者消费完数据,会提交ACK给RocketMQ,然后RocketMQ会修改offset。

kafka是提交offset,而且可以配置是否自动提交。

这种同步推送ACK的机制,结合重试机制(网络故障),能够保证消费消息的过程是不会丢失的。


但是异步消费的情况,在消费到数据后,把数据丢到线程池,然后直接返回ACK。

这种情况可能出现消息丢失问题。(比如消费者机器重启,消息就丢了)

RocketMQ同步刷盘-操作系统

将RocketMQ刷盘方式改为同步,可以解决消息在刷盘过程丢失的问题。也就是第3个问题。

但是同步刷盘会导致写入效率变低,需要有取舍。

比如金融场景要求强一致的情况,可以采用同步刷盘,牺牲写入效率。

而其他场景强一致要求不高的情况下,可以用异步。

Dledger主从架构保证MQ主从同步时不会丢消息

Dledger搭建的集群中,能够实现主从切换和主从数据同步。

普通集群不能实现主从切换,即如果Master挂了,Slave不会转换为Master,进而导致这一组Broker不可用。

Dledger内部能保证数据在主从同步时不会丢失。

NameServer挂掉消息怎么保证不丢失?

当NameServer挂掉之后,RocketMQ没有Broker路由的功能,找不到对应的Broker,就会导致RocketMQ不可用。

这种情况就要做降级处理,如果RocketMQ不可用,在重试几次之后。要将订单信息存到其他地方,比如Redis、内存。等MQ恢复之后,再第一时间处理这些消息。

Dledger的主从同步

image.png

通过两阶段提交的方式保证文件在主从之间同步数据不会丢数据。

  • uncommitted阶段

    • 数据提交给Leader Broker之后,会被标记为uncommitted状态。
    • 然后通过 DledgerServer 将 uncommitted 消息发给 Follower Broker 的 DledgerServer。
  • commited阶段

    Follower Broker 的 DledgerServer 收到 uncommitted消息之后,必须返回一个ack。

    • 如果Leader Broker收到超过半数的 ack ,就会把消息标记为 committed状态。
    • Leader Broker上面的DledgerServer会发送一个committed消息给每个Follower,让他们把消息改为committed状态。

方案总结

  • 生产者使用事务消息保证。
  • 消费者不要使用异步消息。
  • Broker配置同步刷盘。
  • Dledger主从架构保证主从同步数据不会丢。
  • MQ挂了之后准备降级方案。

每个阶段保证不丢失数据的方案,都是牺牲了性能来做的。

在实际处理时要做适当选择。