RocketMQ的Broker是如何持久化存储消息

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RocketMQ的Broker是如何持久化存储消息相关的知识,希望对你有一定的参考价值。

参考技术A 当生产者发送一个消息到Broker上的时候,Broker接受到一条消息之后,会把这个消息直接写到磁盘上的一个日志文件,这个文件叫做CommitLog,直接顺序写入这个CommitLog是很多磁盘文件,每个文件限定最多1GB,Broker收到消息之后直接追加写入这个文件的末尾,如果一个CommitLog写满1GB,就会创建一个新的CommitLog日志。如下图所示:

其实在Broker中, 每个Topic下的每个MessageQueue都会有一系列的ConsumeQueue文件,这个是什么意思呢,就是说Broker的磁盘上,会有下面格式的一系列的文件:/home/store/cousumequeue/topicid/queueid/fileName,这个文件格式各个目录代表的意思就是:对每个Topic在这台Broker上面不都是有一些MessageQueue么,所以topicid就是代表的某个topic,queueid指的就是某个MessageQueue,然后存储在这个Broker上的Topic下的一个MessageQueue会有多个ConsumeQueue文件,这个ConsumeQueue文件中存储的就是一条消息在CommitLog中存放的offset偏移量。是不是有点晕,别急对这句话的解释如下看图:

假设一个消息发送到这个Broker上那么这个Broker上面有两个MessageQueue也就是MessageQueue0和MessageQueue1,那么上图画的ConsumeQueue0和ConsumeQueue1分别就是和MessageQueue0和MessageQueue1对应着。假设这个Topic叫做TopicOrderPaySuccess,那么在磁盘上应该有如下两个路径文件:

然后呢,当Broker接受到一条消息写入CommitLog之后,其实同时也会将这条数据的在CommitLog中存储的物理位置,也就是一个文件偏移量,就是offset写入这条消息所处的MessageQueue对应的ConsumeQueue文件中。
假设一个消息发送到MessageQueue0上面那么Broker就会把这条消息在CommitLog中的偏移量offset写入到这个MessageQueue0所对应的ConsumeQueue0上。ConsumeQueue中的存储的这个物理位置(偏移量offset)其实就是对CommitLog文件的一个消息引用。其实ConsumeQueue文件中不只是存了消息引用,还包含了消息的长度,以及tag,还有hashcode,一条数据是20个字节,每个ConsumeQueue文件保存30万条数据,大概每个文件是5.72MB。

Broker是基于OS操作系统的PageCache和顺序写机制,来提升CommitLog的文件写入性能。顺序写的意思就是Broker每次都是将消息写入CommitLog的文件中时,就是在文件的末尾加一条数据就可以了。顺序写的性能要比随机写的性能高很多倍。还有就是所说的利用os操作系统的PageCache,就是数据写入CommitLog的时候,其实不是直接写入底层的物理磁盘文件的,而是先进入OS的PageCache内存缓存中然后后续有os的后台线程选一个时间,异步化的将OS PagCache内存缓存的数据刷入到物理磁盘中。所以利用 顺序写+OS PageCache写入+异步刷盘策略,可以将写CommitLog文件的性能和写内存的性能差不多。

异步刷入磁盘可能会造成数据的丢失,但是这样的话性能比较高,RocketMQ还支持一种模式就是同步刷盘,就是必须真正的写入到物理磁盘后才会返回ack给生产者,但是这样的话写入性能就会大打折扣。

19 Broker持久化存储消息的原理

1. Broker的重要性

Broker数据存储实际上才是MQ最核心的环节,它决定了生产者消息写入的吞吐量,决定了消息不能丢失,决定了消费者获取消息的吞吐量。

生产者发送到Broker的消息不可能都放在内存里,一旦机器宕机、重启,就会有消息丢失的问题。所以RocketMQ的Broker是需要将消息持久化到磁盘文件的。

2.Broker持久化消息到磁盘的原理

消息写入磁盘CommitLog文件

生产者发送的消息会直接写入磁盘上的日志文件 CommitLog。

CommitLog包含有很多磁盘文件,每个文件限定最多1GB,Broker收到消息之后就直接追加写入这个文件的末尾。如果一个CommitLog写满了1GB,就会创建一个新的CommitLog文件。

MessageQueue与offset偏移位置

在Broker中,对Topic下的每个MessageQueue会有一系列的ConsumeQueue文件,这些文件落在磁盘上。

ConsumeQueue文件的路径格式:$HOME/store/consumequeue/{topic}/{queueId}/{fileName}.

在上面的ConsumeQueue格式中,{topic}指的是某个Topic,{queueId}指的是某个MessageQueue。而在{fileName}这个文件中,它存储的就是一条消息对应在CommitLog文件中的offset偏移量。(一条消息的内容记录在CommitLog中,这条消息在CommitLog的哪个位置也就是偏移量offset记录在ConsumeQueue文件中)

场景说明:

上图中,Topic下有MessageQueue0和MessageQueue1两个在Broker上,他们分别对应Broker所在机器的磁盘上的ConsumeQueue0和ConsumeQueue1。

假设此时Topic为“TopicOrderPaySuccess”,那么Broker磁盘下的两个路径的文件为:

$HOME/store/consumequeue/TopicOrderPaySuccess/MessageQueue0/ConsumeQueue0磁盘文件
$HOME/store/consumequeue/TopicOrderPaySuccess/MessageQueue1/ConsumeQueue1磁盘文件

如果这时Broker收到一条消息写入了CommitLog之后,它同时会将这条消息在CommitLog中的物理位置,也就是一个文件偏移量,就是一个offset,写入到这条消息所属的MessageQueue对应的ConsumeQueue文件中去。

ConsumeQueue

在ConsumeQueue中存储的每条数据不只是消息在CommitLog中的offset偏移量,还包含了消息的长度,以及tag hashcode,一条数据是20个字节,每个ConsumeQueue文件保存30万条数据,大概每个文件是5.72MB。

3.消息写入CommitLog文件的底层原理

写入CommitLog的高性能

在RocketMQ中,消息写入磁盘CommitLog文件的性能近乎内存写性能。

PageCache和顺序写

Broker是基于OS操作系统的PageCache和顺序写两个机制,来提升写入CommitLog文件的性能的。

首先Broker是以顺序的方式将消息写入CommitLot磁盘文件的,也就是每次写入就是在文件末尾追加一条数据就可以了,对文件进行顺序写的性能要比文件随机写的性能提升很多。

另外,数据写入CommitLog文件的时候,其实不是直接写入底层的物理磁盘文件的,而是先进入OS的PageCache内存缓存中,然后后续由OS的后台线程选一个时间,异步化的将OS PageCache内存缓冲中的数据刷入底层的磁盘文件。

总结:整个优化采用的是 磁盘文件顺序写+OS PageCache写入+OS异步刷盘的策略。

4.异步刷盘与同步刷盘

异步刷盘

上述的模式就是异步刷盘模式,生产者把消息发送个Broker,Broker将消息写入OS PageCache中,就直接返回ACK给生产者了。

存在的弊端:如果生产者认为消息写入成功了,但是实际上那条消息此时是在Broker机器上的os cache中的,如果此时Broker宕机,必然导致这里的数据丢失,而Producer还以为数据已经写入成功了,这就有问题了。

总结:在异步刷盘的策略下,可以让消息写入吞吐量非常高。但是可能会有数据丢失的风险。

同步刷盘

同步刷盘模式下,生产者发送一条消息出去,broker收到了消息,必须直接强制把这个消息刷入底层的物理磁盘文件中,然后才会返回ack给producer,此时生产者才知道消息写入成功了。

如果brokerr还没有来得及把数据同步刷入磁盘,然后他自己挂了,那么此时对producer来说会感知到消息发送失败了,然后你只要不停地重试发送就可以了,直到有slave broker切换成 master broker重新让你可以写入消息,此时可以保证数据是不会丢失的。

同步刷屏的优缺点:如果强制每次消息写入都要直接进入磁盘中,必然导致每条消息写入性能急剧下降,导致消息写入吞吐量急剧下降,但是可以保证数据不会丢失。

5.应用场景

异步刷盘:日志场景,允许部分数据丢失,需要高吞吐

同步刷盘:订单场景,不需要高吞吐,需要保证数据不丢失

补偿机制:库存系统,库存明细可以采用异步刷盘,即使极端情况下数据丢失,也可以通过日志做数据的补偿。

小结:

        生产者将消息发给Broker,Broker将消息写入CommitLog,每个CommitLog最多为1G,写完后,重新建一个。

         每个Topic可以配置多个MessageQueue,每个MessageQueue都对应一系列的ConsumeQueue文件,ConsumeQueue文件中记录的是CommitLog消息的offset偏移量。通过ConsumeQueue中记录的offset偏移量定位到CommitLog中的消息。

        Broker有2中刷盘策略:同步刷盘和异步刷盘。

       同步刷盘是指broker收到消息后,必须强制将消息刷入磁盘,才返回ack给producer;异步刷盘是指broker收到消息后,将消息写入OS PageCache后就返回ack给producer。

       同步刷盘消息写入性能低,但是可以保证消息不丢失;异步刷盘消息写入吞吐量高,但是消息可能会丢失。

       CommitLog采用磁盘顺序写+OS PageCache写入+异步刷盘的方式达到近似内存的写入性能。

以上是关于RocketMQ的Broker是如何持久化存储消息的主要内容,如果未能解决你的问题,请参考以下文章

RocketMQ消息存储原理

Rocketmq broker 消息仓库

19 Broker持久化存储消息的原理

RocketMQ

RocketMQ源码(12)—Broker 的消息刷盘源码深度解析一万字

RocketMQ Broker消息存储结构图