RocketMQ 存储设计到底强在哪?

引言对于一款消息中间件来说,优良的数据存储设计,是实现高性能消息吞吐以及消息查询的关键所在 。因为消息中间件对于外部来说就是发消息消费消息的一个平台基础设施,但是从其本身来说,需要将海量消息数据信息持久化在 RocketMQ 节点所在的服务器上,这样即便是服务器断电,重启等情况下,也不至于丢失消息数据 。另外在进行消息消费的时候,RocketMQ 如何能借助自身的存储设计快速检索到对应的消息也是非常重要的,因此本文主要对 RocketMQ 存储设计进行了设计分析 。
存储结构RocketMQ 对应的存储文件主要包括三类,分别是 Commitlog 文件、ConsumeQueue 文件以及 Index 文件,每一个文件都有其特殊的使命 。

RocketMQ 存储设计到底强在哪?

文章插图
 
Commitlog 文件
当生产者将消息发送到 RocketMQ 的 Broker 之后,需要将消息进行持久化存储,防止消息数据丢失 。RocketMQ 将消息数据写入存储文件 CommitLog 中,按照消息的发送顺序写入文件当中,每个文件的大小约为 1G,当达到文件大小限制后,就会创建新的 CommitLog 文件 。RocketMQ 作为消息中间件来说,最主要的数据流程就是基于主题的发布-订阅模式进行消息的发布以及消费,那么当消费者根据自己订阅的 Topic 进行消息消费的时候,Broker 怎么在那么多的 CommitLog 文件中找到对应 Topic 的消息数据呢?
RocketMQ 存储设计到底强在哪?

文章插图
 
大家可以想一想,CommitLog 文件中的消息数据是一条一条顺序写的,最笨的方法就是遍历文件,作为一款高性能的消息中间件,显然这不是一个好的解决方案 。就像从数据库查询数据的时候,遍历的效率肯定是很低的 。那么我们可不可以借助数据库提升数据查询的方式,使用索引来加快消息数据的查询呢?答案是肯定的 。就像 MySQL 中的索引本身需要文件保存一样,在 RocketMQ 中页有单独保存索引的文件,就是 ConsumerQueue 文件 。
ConsumerQueue 文件
在 RocketMQ 中,每个 Topic 对应多个 MessageQueue,每个 MessageQueue 对应一组 ConsumerQueue 文件索引文件 。ConsumerQueue 文件中存储了消息相对于 CommitLog 文件的 offset 偏移量,CommitLog 文件本身实际上也是通过偏移量来进行命名如第一个文件是 0000000000000,那么第二文件就是消息总量之和 00000001232321,往后新的文件再进行累计 。为什么这么做呢?主要就是在进行消息查找的时候根据消息的偏移量通过二分查找快速定位具体的 CommitLog 文件,提升消息查找效率 。需要说明的是,Broker 在进行消息写入 CommitLog 文件中就会异步将其对应的偏移量写入 ConsumerQueue 文件中 。
RocketMQ 存储设计到底强在哪?

文章插图
 
在 ConsumerQueue 文件中实际存储了 CommitLog 文件的 offset 偏移量、消息长度以及 tag 的 hashcode,组成 20 字节的 block 块 。其在 Broker 上面的存储路径大致是:…/store/consumequeue/{topic}/{queueid}/{file} 。其中 topic 就是生产中订阅的主题,因此消费者在消费消息的时候,Broker 会根据其对应的 Topic 找到对应的 ConsumerQueue 文件,进而找到其索引位置,再到 CommitLog 文件中直接定位具体的消息 。
Index 文件
另外 RocketMQ 的特性功能就是可以实现按照消息的属性进行消息搜索,即建立了索引 Key 的 hashcode 与物理偏移量的映射关系,根据 key 先快速定义到 commitlog 文件 。
RocketMQ 存储设计到底强在哪?

文章插图
 
存储性能设计精髓上文中为大家阐述了 RocketMQ 关于存储结构的设计,优秀的存储设计师实现高性能读写的前提 。那么除了存储结构的设计,RocketMQ 也使用了一些性能优化手段来实现其强大的消息吞吐能力 。
消息顺序写
前文中说过,消息进入 RocketMQ 之后,消息数据是通过顺序写的方式落到 CommitLog 文件中的 。那么这里面就涉及两个问题,为什么进行顺序写以及是不是直接写磁盘文件 。
1、为什么要顺序写?
当新的数据到来时,只要在之前的文件末尾进行数据追加就可以,这样的数据写入效率要比随机写入的效率高 。
2、每次数据写入的时候是直接写磁盘文件吗?
我们可以反过想,如果每次都是落盘写入的话实际效率是不高的,无法满足消息中间件这种高吞吐的性能要求,因此 RocketMQ 实际是借助操作系统的 page cache 来提升写入效率的,消息并不是直接写入磁盘,认识先写入操作系统的 page cache,然后再通过异步刷盘的方式,写入 CommitLog 文件中,这样借助顺序写以及系统的 page cache 可以时间近乎内存的数据写入效率 。


推荐阅读