#yyds干货盘点#【Alibaba中间件技术系列】「RocketMQ技术专题」让咱们一同来看看RocketMQ和Kafka索引设计

2022年01月15日 阅读数:2
这篇文章主要向大家介绍#yyds干货盘点#【Alibaba中间件技术系列】「RocketMQ技术专题」让咱们一同来看看RocketMQ和Kafka索引设计,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

背景介绍

文件索引,是存储设计的关键,一个好的索引,应该可以在最短的时间里,找到你想要的数据,同时,还能尽可能少的使用内存或磁盘空间。 可是这里说的索引并非指MySQL或者NoSQL这些数据库索引,而是MQ中间件的索引。相对而言较为简单的MQ索引。咱们能够经过研究MQ的索引,看看他们为什么如此设计,咱们又有哪些借鉴之处,而且也能够根据他们索引文件的设计模式,进行分析他们的性能问题,接下来咱们借来分别说说RocketMQ和Kafka的索引设计原理,重点咱们会介绍RocketMQ的设计。数据库

RocketMQ

相比较Kafka的分区索引文件的设计方案,RocketMQ的数据文件属于混合存储,即,全部的topic数据都放在一个文件里,所以,读数据的时候,就没法作到连续读了,只能随机读。 因此,RocketMQ推荐使用大内存,利用PageCache 预读机制把commitlog数据缓存起来,混合存储的好处则是可以承受万级别的队列数量。 kafka 64分区有些夸张,单机单磁盘1000分区仍是没啥问题的,经验之谈最好别超过 2000, RocketMQ 提供基于MsgID搜索消息的方案,即,每条消息,都有一个惟一的 ID,设计模式

Message ID

ID由broker IP + Port + CommitLog Offset 组成,经过这两个参数,可快速定位到一条消息。注意,Kafka是没有这个功能的,但理论上,经过 Kafka 的 offset 也是能够找到具体的消息的。缓存

另外 RocketMQ 有 2 种索引。

  • 消息消费索引
  • Hash 查询索引

消息消费索引

消息消费索引,能够理解为,就是 topic 的索引数据,相似 kafka 的索引数据。若是没有这个,消费者基本就找不到消息了。这个索引里,存放着对应topic 、对应 queue 里的消息连续 offset 集合(不像 commitLog 是混合存储的)。架构

#yyds干货盘点#【Alibaba中间件技术系列】「RocketMQ技术专题」让咱们一同来看看RocketMQ和Kafka索引设计_kafka

RocketMQ的存储层架构

#yyds干货盘点#【Alibaba中间件技术系列】「RocketMQ技术专题」让咱们一同来看看RocketMQ和Kafka索引设计_kafka_02

RocketMQ 的运做流程图

#yyds干货盘点#【Alibaba中间件技术系列】「RocketMQ技术专题」让咱们一同来看看RocketMQ和Kafka索引设计_kafka_03

RocketMQ 的存储设计图:

消息被不停的 append 到 commitlog,而后,再构建消费索引,若是没有这个索引,consumer 要在 commitlog 里消费消息,那可真是太难了。app

#yyds干货盘点#【Alibaba中间件技术系列】「RocketMQ技术专题」让咱们一同来看看RocketMQ和Kafka索引设计_偏移量_04

每一个consumerQueue文件里存放着 30w 个元素,每一个元素 20 字节,8 字节 offset ,4 字节 size, 8 字节 tag hashcode,所以,每一个文件也就 5.8MB 不到,很轻量。ide

#yyds干货盘点#【Alibaba中间件技术系列】「RocketMQ技术专题」让咱们一同来看看RocketMQ和Kafka索引设计_偏移量_05

Hash查询索引(咱们能够称之为tag)

Hash查询索引,主要是根据 Key 来快速查询消息,属于一种附加功能。RocketMQ 采用了 Java HashMap 的思想,实现了 Hash 索引的存储。性能

  • 若是这个 Map 有 500w 个 slot,每一个 slot 的链表长度为 4. 若是咱们使用一个 key 进行消息查找,他的过程是这样的:先 hash key 获得 hashCode,而后对 500w 取余,找到槽位,这个槽位大小是4个字节,保存了链表尾部的具体元素地址。
  • 而这个链表元素的大小是 20 个字节,保存了 key 的 hash 值,commitlog offset,时间戳,还有他下一个链表节点的地址。
  • 为何在 链表元素里保存 了 hash 值呢?为了防止 hash 值不一样,可是 hash 取模后的结果相同(也就是 hash 冲突),若是冲突了,就用 hash 值比对一下。
  • 那若是 hash 值相同,key 内容不一样呢?RocketMQ 的作法是放在客户端过滤。

简单介绍一下Kafka

Kafka 每一个 topic 有多个 partition ,每一个 partition 有多个 segment,每一个 segment 里,存储了消息的相关文件:数据文件,索引文件。 Kafka 不像 RocketMQ,全部数据都存在一个文件里,Kafka 每一个 topic 的文件都是隔离开的,而每一个 topic 又可能会有不少的 partition(看你的配置),所以,若是你的topic很是多,或者你的partition很是多的话,顺序写就会变成随机写,性能会骤降。设计

Kafka 的索引文件和数据文件绑定在一块儿的。

与RocketMQ的消费索引相似,Kafka 里面是逻辑 offset 映射物理 offset ,而且采用了稀疏索引的方式。而后,咱们看看他们的索引设计,以下图: [逻辑索引,偏移量]3d

  • 逻辑索引,即这个 partition下的全局递增逻辑索引(固然,这个是相对偏移量,这里为了描述简单,就不区分了)
  • 偏移量,表示这条消息的所在文件的物理 position。

我如今是一个消费者,订阅了这个 partition 的消息,那么我将从 0 号逻辑索引开始订阅,从.index 开始遍历,而后找到对应的物理文件position。 kafka 的这个 .index 文件和 RocketMQ 的 consumerQueue 索引很类似,直接遍历 .log 文件,从头开始消费。但若是,我不想从头开始消费呢?我想从第 18 条消息开始消费呢?由于没有 .index ,我只能慢慢遍历。 一个 topic 设计一个递增的 offset,从 0 开始,每新增一条消息,加一。这是一个逻辑偏移量,咱们让逻辑偏移量 映射 物理偏移量。消费者也从 0 开始消费,这样,就达到了某种默契。就算是第 18 条消息,我也能快速找到。code

基于 partition 的分区原子计数器。使用 broker ID + 分区 ID + 计数器 就能够标识一条惟一的消息。而后,用计数器映射 偏移量 offset,简直就是完美。而后,为了达到搜索效率和空间消耗的平衡,边稠密索引为稀疏索引。

RocketMQ 和 Kafka 的索引设计类似之处:

RocketMQ 的 topic 和 kafka 的 topic 相似,RocketMQ 的 queue 和 kafka 的 partition 相似,都是为了 scale out。

  • RocketMQ 为每一个 queue 设计了 consumerQueue 索引文件,每一个文件大小固定 5.8MB;
  • Kafka 为每一个 partition 设计了 segment (.index + .log)。

consumerQueue 索引文件和 segment 的 .index 本质是同样的,都是为了让 consumer 快速找到消息。

和 Kafka 的索引设计的最大不一样

RocketMQ 是全部 topic 混合存储,目的是支持更多的topic,而 Kafka 的topic 是单独存储,好处是顺序读性能好,另外,根据分区作副本也比较好作。