前言
RocketMQ的是一款追求低延迟
的消息队列,虽然他是存储在磁盘
上的,但是他的读写性能还是非常之高,本文将分析他的存储设计,看看他是从哪些方面对性能有改善。
RocketMQ存储组成
我们可以把RocketMQ的Broker
理解成一个数据库
一样,数据库存储功能主要是读和写
的功能,RocketMQ也一样。在数据库中,我们将数据写入数据和索引中,在RocketMQ中,我们将数据写到commitlog
文件和consumequeue
文件。RocketMQ数据和索引默认都存储在user.home
目录下的store
文件夹。
从上图可以看出,在RocketMQ中,数据存储存储在commitlog文件夹,就是消息内容存储,消息内容是通过commitlog文件存储的,并且消息内容是混合存储
的,里面包含了所有topic
的消息数据。
而索引有两部分,一个是consumequeue, 一个是index文件夹。
consumequeue
部分是先按topic,topic下按队列进行分开存储的。我们一般是按topic消费我们的消息。这个时候consumequeue就可以派上用场了。
index索引文件
是存储支持时间和消息key来检索消息的索引数据。
commitlog文件结构和写操作
发送消息到broker后,broker会开始写commitlog文件。在CommitLog
类有对commitlog文件的写操作。
看下CommitLog
的组成,里面包含了MappedFileQueue
MappedFileQueue
里面包含了文件数组,对应多个commitlog文件,每个commitlog文件默认存储1G大小的消息。
我们再看下MappedFileQueue里面真实操作文件的的对象其实是MappedFile
。
看MappedFile
的构造函数会调用init方法,里面就是通过RandomAccessFile
创建commitlog的文件对象,并且将文件映射到内存MappedByteBuffer
,也就是每次都是把消息先写入内存缓冲区,再写入磁盘。
发送消息的时候,其实会操作MappedFile
将消息写入内存缓冲区MappedByteBuffer
。这就是RocketMQ发消息快的一个原因。
最终刷新到磁盘是怎么做的呢? 会通过mappedByteBuffer.force()
函数刷新到磁盘。
commitlog文件结构和读操作
读操作体现在查找消息的方法,在org.apache.rocketmq.store.CommitLog#getMessage
函数。
首先是根据偏移量查找MapperFile
,
最终通过MapperFile
查询消息内容。
consumeQueue组成以及读写操作
看到ConsumeQueue
类里面的组成,和commitlog一样,同样持有了MappedFileQueue
,那么读写consumeQueue
,也是操作MappedFile
。
这样我们也能知道consumequeue的组成
需要注意的是,consumequeue
不是同步构建的。RocketMQ专门设计了一个任务ReputMessageService
。 他是异步将consumequeue数据构建出来,并且使用了一个异步线程FlushConsumeQueueService
将consumequeu数据刷入磁盘。
也就是索引数据都是异步构建出来的。这个也是RocketMQ消息存储性能极高的原因。
总结
1、RocketMQ存储模块包含消息数据commitlog
和消息索引consumequeu
部分,他们都会将文件映射到内存,不会直接操作磁盘,这样做提高了IO效率。
2、消息数据comitlog
是先写入内存缓存区,再异步刷新磁盘,而消息索引consumequeue
是通过异步构建的