态度决定一切

0%

RocketMQ数据存储结构详解

1 RocketMQ消息存储结构

Broker 整体架构图

消息体结构

官网地址

1.1 commitLog (类似Mysql的redolog)

消息存放的物理文件,是消息主体以及元数据存储的主体。每个broker上的 CommitLog被本机所有的Consumequeue共享,用于存储Producer端写入的消息主体内容,消息内容不是定长的,文件顺序写,随机读。单个文件大小默认1G 可以配置,文件名长度为20位,左边补零,剩余为起始偏移量,比如00000000000000000000代表了第一个文件,起始偏移量为0,文件大小为1G=1073741824;当第一个文件写满了,第二个文件为00000000001073741824,起始偏移量为1073741824,以此类推。消息主要是顺序写入日志文件,当文件满了,写入下一个文件。

commitlog存储单元结构图

1.2 ConsumeQueue


消息消费队列,引入的目的主要是提高消息消费的性能,由于RocketMQ是基于主题topic的订阅模式,消息消费是针对主题进行的,如果要遍历commitlog文件中根据topic检索消息是非常低效的。Consumer即可根据ConsumeQueue来查找待消费的消息。其中,ConsumeQueue(逻辑消费队列)作为消费消息的索引,保存了指定Topic下的队列消息在CommitLog中的起始物理偏移量offset,消息大小size和消息Tag的HashCode值。consumequeue文件可以看成是基于topic的commitlog索引文件,故consumequeue文件夹的组织方式如下:topic/queue/file三层组织结构,具体存储路径为:$HOME/store/consumequeue/{topic}/{queueId}/{fileName}。同样consumequeue文件采取定长设计,每一个条目共20个字节,分别为8字节的commitlog物理偏移量、4字节的消息长度、8字节tag hashcode,单个文件由30W个条目组成,可以像数组一样随机访问每一个条目,每个ConsumeQueue文件大小约5.72M;

consumerQueue 存储单元格结构

1.3 IndexFile

消息索引文件,Index 索引文件提供了对 CommitLog 进行数据检索,提供通过 key 或者时间区间来查询 CommitLog 中的消息的方法。在实际的物理存储上,文件名则是以创建时的时间戳命名的,固定的单个IndexFile文件大小约为400M,一个IndexFile可以保存 2000W个索引

IndexFile结构分析

IndexHead

  • beginTimestamp:该索引文件包含消息的最小存储时间
  • endTimestamp:该索引文件包含消息的最大存储时间
  • beginPhyoffset:该索引文件中包含消息的最小物理偏移量(commitlog 文件偏移量)
  • endPhyoffset:该索引文件中包含消息的最大物理偏移量(commitlog 文件偏移量)
  • hashSlotCount:hashslot个数,并不是 hash 槽使用的个数,在这里意义不大,
  • indexCount:已使用的 Index 条目个数

Hash 槽

  • 一个 IndexFile 默认包含 500W 个 Hash 槽,每个 Hash 槽存储的是落在该 Hash 槽的 hashcode 最新的 Index 的索引

Index 条目列表

  • hashcode:key 的 hashcode
  • phyoffset:消息对应的物理偏移量
  • timedif:该消息存储时间与第一条消息的时间戳的差值,小于 0 表示该消息无效
  • preIndexNo:该条目的前一条记录的 Index 索引,hash 冲突时,根据该值构建链表结构

1.5 消息发送到消息消费数据流转

  1. Producer 将消息发送到 Broker 后,Broker 会采用同步或者异步的方式把消息写入到 CommitLog。RocketMQ 所有的消息都会存放在 CommitLog 中,为了保证消息存储不发生混乱,对 CommitLog 写之前会加锁,同时也可以使得消息能够被顺序写入到 CommitLog,只要消息被持久化到磁盘文件 CommitLog,那么就可以保证 Producer 发送的消息不会丢失
  2. CommitLog 持久化后,会把里面的消息 Dispatch 到对应的 Consume Queue 上,Consume Queue 相当于 Kafka 中的 Partition,是一个逻辑队列,存储了这个 Queue 在 CommitLog 中的起始 Offset,log 大小和 MessageTag 的 hashCode
  3. 当消费者进行消息消费时,会先读取 ConsumerQueue,逻辑消费队列 ConsumeQueue 保存了指定 Topic 下的队列消息在 CommitLog 中的起始物理偏移量 Offset,消息大小、和消息 Tag 的 HashCode 值。
  4. 直接从 ConsumerQueue 中读取消息是没有数据的,真正的消息主体在 CommitLog 中,所以还需要从 CommitLog 中读取消息。

1.6 消息存储流程代码流程

  • Broker端收到消息后,将消息原始信息保存在CommitLog文件对应的MappedFile中,然后异步刷新到磁盘
  • ReputMessageServie线程异步的将CommitLog中MappedFile中的消息保存到ConsumerQueue和IndexFile中
  • ConsumerQueue和IndexFile只是原始文件的索引信息

2 同步刷盘和异步刷盘

刷盘流程

  • producer发送给broker的消息保存在MappedFile中,然后通过刷盘机制同步到磁盘中
  • 刷盘分为同步刷盘和异步刷盘
  • 异步刷盘后台线程按一定时间间隔执行
  • 同步刷盘也是生产者-消费者模型。broker保存消息到MappedFile后,创建GroupCommitRequest请求放入列表,并阻塞等待。后台线程从列表中获取请求并刷新磁盘,成功刷盘后通知等待线程。


(1) 同步刷盘:如上图所示,只有在消息真正持久化至磁盘后RocketMQ的Broker端才会真正返回给Producer端一个成功的ACK响应。同步刷盘对MQ消息可靠性来说是一种不错的保障,但是性能上会有较大影响,一般适用于金融业务应用该模式较多。

(2) 异步刷盘:能够充分利用OS的PageCache的优势,只要消息写入PageCache即可将成功的ACK返回给Producer端。消息刷盘采用后台异步线程提交的方式进行,降低了读写延迟,提高了MQ的性能和吞吐量。

读取消息的ConsumeQueue文件也会加载到PageCache,读PageCache和内存速度差不多。

2.1 刷盘方法调用流程

  • producer发送给broker的消息保存在MappedFile中,然后通过刷盘机制同步到磁盘中
  • 刷盘分为同步刷盘和异步刷盘
  • 异步刷盘后台线程按一定时间间隔执行
  • 同步刷盘也是生产者-消费者模型。broker保存消息到MappedFile后,创建GroupCommitRequest请求放入列表,并阻塞等待。后台线程从列表中获取请求并刷新磁盘,成功刷盘后通知等待线程

3 同步复制和异步复制

3.1 异步复制

消息写到 Broker 后,直接返回客户端成功,消息数据异步到 Slave 节点
优点:性能高,不需要等到消息同步直接返回。适合能容忍消息丢失的场景
缺点:这种可能会导致消息丢失

3.2 同步复制

消息写到Broker 后,需要将消息同步复制到 Slave 节点才返回成功
优点:能够保证消息绝对不丢失,保证高可用,这种适合和金融相关的业务
缺点:性能不高,因为需要将消息同步到 slave 才能能返回成功

4 高可用机制

4.1 Broker 集群

名称 描述 优点 缺点
单个 Master 这种方式风险较大,一旦Broker 重启或者宕机时,会导致整个服务不可用,不建议线上环境使用 ~ ~
多 Master 模式 一个集群无 Slave,全是 Master,例如 2 个 Master 或者 3 个 Master, 消息分别写到不同的 master 节点上 配置简单,单个Master 宕机或重启维护对应用无影响,在磁盘配置为 RAID10 时,即使机器宕机不可恢复情况下,由与 RAID10 磁盘非常可靠,消息也不会丢(异步刷盘丢失少量消息,同步刷盘一条不丢)。性能最高 单台机器宕机期间,这台机器上未被消费的消息在机器恢复之前不可订阅,消息实时性收到影响
多 Master 多 Slave 模式,异步复制 每个 Master 配置一个 Slave,有多对Master-Slave,HA 采用异步复制方式,主备有短暂消息延迟,毫秒级。消息写入 Master 节点,异步复制到 Slave节点 即使磁盘损坏,消息丢失的非常少,且消息实时性不会受影响,因为 Master 宕机后,消费者仍然可以从 Slave 消费,此过程对应用透明。不需要人工干预。性能同多 Master 模式几乎一样 Master 宕机,磁盘损坏情况,会丢失少量消息
多 Master 多 Slave 模式,同步双写 每个 Master 配置一个 Slave,有多对Master-Slave,HA 采用同步双写方式,master和slave都写成功,向应用返回成功。 数据与服务都无单点,Master宕机情况下,消息无延迟,服务可用性与数据可用性都非常高 性能比异步复制模式略低,大约低 10%左右,发送单个消息的 RT 会略高。目前主宕机后,备机不能自动切换为主机,后续会支持自动切换功能。

4.2 Dledger

支持故障转移,自动将 slave 节点提升为 master 提供服务