RocketMQ和Kafka消息存储对比

共同点:

对比这两种存储结构,它们有很多共通的地方,都是采用消息文件 + 索引文件的存储方式,索引文件的名字都是第一条消息的索引序号,索引中记录了消息的位置等等。
写入消息的时候,消息按照自然顺序追加写入到同一个消息文件中,一个文件写满了再写下一个文件。

差异点:

Kafka:

采用的是稀疏索引,为了节省存储空间,它不会为每一条消息都创建索引,而是每隔几条消息创建一条索引。

Kafka 的存储以Partition为单位,每个Partition包含一组消息文件(Segment file)和一组索引文件(Index),并且消息文件和索引文件一一对应,具有相同的文件名(但文件扩展名不一样),文件名就是这个文件中第一条消息的索引序号。
查找消息时,首先根据文件名找到所在的索引文件,然后用二分法遍历索引文件内的索引,在里面找到离目标消息最近的索引,再去消息文件中,找到这条最近的索引指向的消息位置,从这个位置开始顺序遍历消息文件,找到目标消息。寻址操作较耗时

RocketMQ:

采用的是定长稠密索引,它为每一条消息都建立索引,每个索引的长度(注意不是消息长度)是固定的 20 个字节。

RocketMQ的存储以Broker为单位。它的存储也是分为消息文件和索引文件,但是在RocketMQ中,每个Broker只有一组消息文件,它把在这个Broker上的所有主题的消息都存在这一组消息文件中。索引文件和Kafka一样,是按照主题和队列分别建立的,每个队列对应一组索引文件,这组索引文件在 RocketMQ 中称为 ConsumerQueue。
查找消息的时候,可以直接根据队列的消息序号,计算出索引的全局位置(索引序号 x 索引固定长度 20),然后直接读取这条索引,再根据索引中记录的消息的全局位置,找到消息。这里的两次都是绝对寻址,寻址速度很快

优缺点分析:

在消息文件的存储粒度上,Kafka以分区为单位,粒度更细,优点是更加灵活,很容易进行数据迁移和扩容。RocketMQ以Broker为单位,较粗的粒度牺牲了灵活性,带来的好处是,在写入的时候,同时写入的文件更少,有更好的批量(不同主题和分区的数据可以组成一批一起写入),更多的顺序写入,尤其是在 Broker 上有很多主题和分区的情况下,有更好的写入性能。

索引设计上,RocketMQ和Kafka分别采用了稠密和稀疏索引,稠密索引需要更多的存储空间,但查找性能更好,稀疏索引能节省一些存储空间,代价是牺牲了查找性能。
可以看到,两种消息队列在存储设计上,有不同的选择。

大多数场景下,这两种存储设计的差异其实并不明显,都可以满足需求。但是在某些极限场景下,依然会体现出它们设计的差异。比如,在一个Broker上有上千个活动主题的情况下,RocketMQ的写入性能就会体现出优势。再比如,如果我们的消息都是几个、十几个字节的小消息,但是消息的数量很多,这时候Kafka的稀疏索引设计就能节省非常多的存储空间。