08第三章:02_消息的存储
作者:互联网
一、消息的存储
RocketMQ 中的消息存储在本地文件系统中,这些相关文件默认在当前用户主目录下的 store 目录中。
- abort:该文件在 Broker 启动后会自动创建,正常关闭 Broker,该文件会自动消失。若在没有启动 Broker 的情况下,发现这个文件是存在的,则说明之前 Broker 的关闭是非正常关闭。
- checkpoint:其中存储着 commitlog、consumequeue、index 文件的最后刷盘时间戳
- commitlog:其中存放着 commitlog 文件,而消息是写在 commitlog 文件中的
- config:存放着 Broker 运行期间的一些配置数据
- consumequeue:其中存放着 consumequeue 文件,队列就存放在这个目录中
- index:其中存放着消息索引文件 indexFile
- lock:运行期间使用到的全局资源锁
二、commitlog 文件
说明:在很多资料中 commitlog 目录中的文件简单就称为 commitlog 文件。但在源码中,该文件被命名为 mappedFile。
1、目录与文件
commitlog 目录中存放着很多的 mappedFile 文件,当前 Broker 中的所有消息都是落盘到这些 mappedFile 文件中的。
mappedFile 文件大小为 1G(小于等于 1G),文件名由 20 位十进制数构成,表示当前文件的第一条消息的起始位移偏移量。
第一个文件名一定是 20 位 0 构成的。因为第一个文件的第一条消息的偏移量 commitlog offset 为 0
当第一个文件放满时,则会自动生成第二个文件继续存放消息。假设第一个文件大小是 1073741820 字节(1G = 1073741824 字节),则第二个文件名就是 00000000001073741820。
以此类推,第 n 个文件名应该是前 n-1 个文件大小之和。
一个 Broker 中所有 mappedFile 文件的 commitlog offset 是连续的
需要注意的是,一个 Broker 中仅包含一个 commitlog 目录,所有的 mappedFile 文件都是存放在该目录中的。即无论当前 Broker 中存放着多少 Topic 的消息,这些消息都是被顺序写入到了 mappedFile 文件中的。也就是说,这些消息在 Broker 中存放时并没有被按照 Topic 进行分类存放。
mappedFile 文件是顺序读写的文件,所有其访问效率很高。无论是 SSD 磁盘还是 SATA 磁盘,通常情况下,顺序存取效率都会高于随机存取。
2、消息单元
mappedFile 文件内容由一个个的消息单元构成。每个消息单元中包含消息总长度 MsgLen、消息的物理位置 physicalOffset、消息体内容 Body、消息体长度 BodyLength、消息主题 Topic、Topic 长度 TopicLength、消息生产者 BornHost、消息发送时间戳 BornTimestamp、消息所在的队列 QueueId、消息在 Queue 中存储的偏移量 QueueOffset 等近 20 余项消息相关属性。
需要注意到,消息单元中是包含 Queue 相关属性的。所以,我们在后续的学习中,就需要十分 留意 commitlog 与 queue 间的关系是什么
一个 mappedFile 文件中第 m+1 个消息单元的 commitlog offset 偏移量
L(m+1) = L(m) + MsgLen(m) (m >= 0)
三、consumequeue
1、目录与文件
为了提高效率,会为每个 Topic 在~/store/consumequeue 中创建一个目录,目录名为 Topic 名称。在该 Topic 目录下,会再为每个该 Topic 的 Queue 建立一个目录,目录名为 queueId。每个目录中存放着若干 consumequeue 文件,consumequeue 文件是 commitlog 的索引文件,可以根据 consumequeue 定位到具体的消息。
consumequeue 文件名也由 20 位数字构成,表示当前文件的第一个索引条目的起始位移偏移量。与 mappedFile 文件名不同的是,其后续文件名是固定的。因为 consumequeue 文件大小是固定不变的。
2、索引条目
每个 consumequeue 文件可以包含 30w 个索引条目,每个索引条目包含了三个消息重要属性:消息在 mappedFile 文件中的偏移量 CommitLog Offset、消息长度、消息 Tag 的 hashcode 值。这三个属性占 20 个字节,所以每个文件的大小是固定的 30w * 20 字节。
一个 consumequeue 文件中所有消息的 Topic 一定是相同的。但每条消息的 Tag 可能是不同的。
四、对文件的读写
1、消息写入
一条消息进入到 Broker 后经历了以下几个过程才最终被持久化。
- Broker 根据 queueId,获取到该消息对应索引条目要在 consumequeue 目录中的写入偏移量,即 QueueOffset
- 将 queueId、queueOffset 等数据,与消息一起封装为消息单元
- 将消息单元写入到 commitlog
- 同时,形成消息索引条目
- 将消息索引条目分发到相应的 consumequeue
2、消息拉取
当 Consumer 来拉取消息时会经历以下几个步骤:
-
Consumer 获取到其要消费消息所在 Queue 的消费偏移量 offset,计算出其要消费消息的消息 offset
消费 offset 即消费进度,consumer 对某个 Queue 的消费 offset,即消费到了该 Queue 的第几条消息
消息 offset = 消费 offset + 1
-
Consumer 向 Broker 发送拉取请求,其中会包含其要拉取消息的 Queue、消息 offset 及消息 Tag。
-
Broker 计算在该 consumequeue 中的 queueOffset
queueOffset = 消息 offset * 20 字节
-
从该 queueOffset 处开始向后查找第一个指定 Tag 的索引条目。
-
解析该索引条目的前 8 个字节,即可定位到该消息在 commitlog 中的 commitlog offset
-
从对应 commitlog offset 中读取消息单元,并发送给 Consumer
3、性能提升
RocketMQ 中,无论是消息本身还是消息索引,都是存储在磁盘上的。其不会影响消息的消费吗?当然不会。其实 RocketMQ 的性能在目前的 MQ 产品中性能是非常高的。因为系统通过一系列相关机制大大提升了性能。
首先,RocketMQ 对文件的读写操作是通过mmap 零拷贝进行的,将对文件的操作转化为直接对内存地址进行操作,从而极大地提高了文件的读写效率。
其次,consumequeue 中的数据是顺序存放的,还引入了PageCache 的预读取机制,使得对 consumequeue 文件的读取几乎接近于内存读取,即使在有消息堆积情况下也不会影响性能。
PageCache 机制,页缓存机制,是 OS 对文件的缓存机制,用于加速对文件的读写操作。一般来 说,程序对文件进行顺序读写的速度几乎接近于内存读写速度,主要原因是由于 OS 使用 PageCache 机制对读写访问操作进行性能优化,将一部分的内存用作 PageCache。
写操作:OS 会先将数据写入到 PageCache 中,随后会以异步方式由 pdæush(page dirty fush) 内核线程将 Cache 中的数据刷盘到物理磁盘
读操作:若用户要读取数据,其首先会从 PageCache 中读取,若没有命中,则 OS 在从物理磁 盘上加载该数据到 PageCache 的同时,也会顺序对其相邻数据块中的数据进行预读取
RocketMQ 中可能会影响性能的是对 commitlog 文件的读取。因为对 commitlog 文件来说,读取消息时会产生大量的随机访问,而随机访问会严重影响性能。不过,如果选择合适的系统 IO 调度算法,比如设置调度算法为 Deadline(采用 SSD 固态硬盘的话),随机读的性能也会有所提升。
五、与 Kafka 的对比
RocketMQ 的很多思想来源于 Kafka,其中 commitlog 与 consumequeue 就是。
RocketMQ 中的 commitlog 目录与 consumequeue 的结合就类似于 Kafka 中的 partition 分区目录。mappedFile 文件就类似于 Kafka 中的 segment 段。
Kafka 中的 Topic 的消息被分割为一个或多个 partition。partition 是一个物理概念,对应到系统上就是 topic 目录下的一个或多个目录。每个 partition 中包含的文件称为 segment,是具体存放消息的文件。
Kafka 中消息存放的目录结构是:topic 目录下有 partition 目录,partition 目录下有 segment 文件。
Kafka 中没有二级分类标签 Tag 这个概念
Kafka 中无需索引文件。因为生产者是将消息直接写在了 partition 中的,消费者也是直接从 partition 中读取数据的
标签:02,文件,第三章,08,Broker,消息,consumequeue,commitlog,目录 来源: https://www.cnblogs.com/niujifei/p/16536834.html