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

无天有壁纸 2024-05-05 22:38:29
CommitLog消息顺序写入机制

首先思考一下,当生产者的消息发送到一个Broker上的时候,他接收到了一条消息,接着他会对这个消息做什么事情?

首先第一步,他会把整个消息直接写入磁盘上的一个日志文件,叫做CommitLog,直接顺序写入这个文件。

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

MessageQueue在数据存储中是体现在哪里?

如果写入这个Broker的消息都是进入到CommitLog中去存储的,那么前面提到的MessageQueue是体现在哪里呢?

其实在Broker中,对于Topic下的每个MessageQueue都会有一系列的ConsumerQueue文件;这是什么意思呢?就是在Broker的磁盘上,会有下面这种格式的一系列文件:

$HOME/store/consumerqueue/{topic}/{queueid}/{fileName}

上面那串是什么意思?

对于每个Topic不是在这台Broker上都会有一些MessageQueue吗,所以你会看到,{topic}指的就是某个topic,{queueId}指的就是某个MessageQueue。

然后对存储在这台Broker机器上的Topic下的一个MessageQueue,他有很多的ConsumeQueue文件,这个ConsumerQueue文件里存储的是一条消息对应在CommitLog文件中的offset偏移量。

首先我们假设有一个Topic,他有4个MessageQueue,然后再两台Broker机器上,每台Broker机器会存储两个MessageQueue。那么此时假设生产者选择其中一个MessageQueue写入了一条数据,此时消息会发送到Broker上。

然后Broker必然会把整个消息写入自己的CommitLog文件中

然后继续看下图,在图中加入了两个ConsumerQueue,分别叫做ConsumerQueue0和ConsumerQueue1,他们分别对应着Topic里面的MessageQueue0和MessageQueue1.

也就是说,Topic下的MessageQueue0和MessageQueue1就放在这个Broker机器上,而且他们每个MessageQueue目前在磁盘上就对应了一个ConsumeQueue,所以就是MessageQueue0对应着Broker磁盘上的ConsumeQueue0,MessageQueue1对应着磁盘上的ConsumeQueue1。

接着假设Topic的名字叫做TopicOrderPaySuccess,那么此时在Broker磁盘上应该有如下两个路径的文件:

$HOME/store/consumerqueue/TopicOrderPaySuccess/MessageQueue0/ConsumeQueue0磁盘文件

$HOME/store/consumerqueue/TopicOrderPaySuccess/MessageQueue1/ConsumeQueue1磁盘文件

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

比如现在这条消息在生产者发送得到时候是发送给MessageQueue0的,那么此时Broker就会将这条消息在CommitLog中的offset偏移量写入到MessageQueue0对应的ConsumeQueue0中去。如下图所示:

所以实际上,ConsumeQueue0中存储的是一个一个消息在CommitLog文件中的物理位置,也就是offset。同时也包含了消息的长度、以及tag,一条数据是20字节,每个ConsumeQueue文件保存30万条数据,大概每个文件是5.72MB。

所以Topic的每个MessageQueue都对应了Broker机器上的多个ConsumeQueue文件,保存了这个MessageQueue的所有消息在CommitLog文件中的物理位置,也就是offset偏移量。

如何让消息写入CommitLog文件近乎内存写性能的

对于生产者把消息写入到Broker时,Broker会直接把消息写入磁盘上的CommitLog文件,那么Broker是如何提升整个过程的性能的呢?

因为这个部分的性能提升会直接提升Broker处理消息写入的吞吐量,比如你写入一条消息到CommitLog磁盘文件假设需要10ms,那么每个线程每秒可以处理100个写入消息,加入有100个线程,每秒只能处理1万个写入消息请求。

但是如果你把消息写入CommitLog磁盘文件的性能优化为只需要1ms,那么每个线程每秒可以处理1000个消息写入,此时100个线程每秒可以处理10万个写入消息请求。所以可以明显看到,Broker把接收到的消息写入CommitLog磁盘文件的性能,对他的TPS有很大影响。

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

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

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

所以在这样的优化下,采用磁盘文件顺序写+OS PageCache写入+OS异步刷盘的策略,基本上可以让消息写入CommitLog的性能跟你直接写入内存里是差不多的,所以才可以让Broker高吞吐的处理每秒大量的消息写入。

同步刷盘与异步刷盘

如果采用上述的模式,不就是异步刷盘的模式吗?

对的,在上述的异步刷盘模式下,生产者把消息发送给Broker,Broker将消息写入OS PageCache中,就直接返回ACK给生产者了。此时生产者就认为消息写入成功了,那么会有什么问题吗?

问题就是如果生产者认为消息写入成功了,但是实际上那条消息此时是在Broker机器的os cache中的,如果此时Broker直接宕机,那么是不是os cache中的这条数据就会丢失了?

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

另外一种模式叫做同步刷盘,如果使用的是同步刷盘模式的话,那么生产者发送一条消息出去,broker收到了消息,必须直接强制把这个消息刷入底层的物理磁盘文件中,才会返回ack给producer,此时你才知道消息写入成功了。

只要消息进入了物理磁盘上,那么除非是你的物理磁盘坏了导致数据丢失,否则正常来说数据就不会丢失了。

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

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

0 阅读:2

无天有壁纸

简介:感谢大家的关注