Kafka的索引机制

本文深入剖析Kafka的索引机制,包括偏移量索引和时间戳索引的工作原理,以及它们如何提高消息查找效率。同时对比了Kafka与InnoDB的索引机制,解释了Kafka为何选择特定的索引方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

以下文章来源于朱小厮的博客 ,作者朱小厮。

kafka索引机制

       在kafka中,每个日志分段文件都对应了两个索引文件——偏移量索引文件和时间戳索引文件(还有其它的诸如事务日志索引文件就不细表了),主要用来提高查找消息的效率。偏移量索引文件用来建立消息偏移量(offset)到物理地址之间的映射关系,方便快速定位消息所在的物理文件位置;时间戳索引文件则根据指定的时间戳(timestamp)来查找对应的偏移量信息。

       Kafka 中的索引文件以稀疏索引(sparse index)的方式构造消息的索引,它并不保证每个消息在索引文件中都有对应的索引项。每当写入一定量(由 broker 端参数 log.index.interval.bytes 指定,默认值为 4096,即 4KB)的消息时,偏移量索引文件和时间戳索引文件分别增加一个偏移量索引项和时间戳索引项,增大或减小 log.index.interval.bytes 的值,对应地可以缩小或增加索引项的密度。

       稀疏索引通过 MappedByteBuffer 将索引文件映射到内存中,以加快索引的查询速度。偏移量索引文件中的偏移量是单调递增的,查询指定偏移量时,使用二分查找法来快速定位偏移量的位置,如果指定的偏移量不在索引文件中,则会返回小于指定偏移量的最大偏移量。时间戳索引文件中的时间戳也保持严格的单调递增,查询指定时间戳时,也根据二分查找法来查找不大于该时间戳的最大偏移量,至于要找到对应的物理文件位置还需要根据偏移量索引文件来进行再次定位。稀疏索引的方式是在磁盘空间、内存空间、查找时间等多方面之间的一个折中。

      以偏移量索引文件来做具体分析。偏移量索引项的格式如下图所示。每个索引项占用 8 个字节,分为两个部分:(1) relativeOffset: 相对偏移量,表示消息相对于 baseOffset 的偏移量,占用 4 个字节,当前索引文件的文件名即为 baseOffset 的值。(2) position: 物理地址,也就是消息在日志分段文件中对应的物理位置,占用 4 个字节。

                             

       消息的偏移量(offset)占用 8 个字节,也可以称为绝对偏移量。索引项中没有直接使用绝对偏移量而改为只占用 4 个字节的相对偏移量(relativeOffset = offset - baseOffset),这样可以减小索引文件占用的空间。举个例子,一个日志分段的 baseOffset 为 32,那么其文件名就是 00000000000000000032.log,offset 为 35 的消息在索引文件中的 relativeOffset 的值为 35-32=3。

              

       如果我们要查找偏移量为 23 的消息,那么应该怎么做呢?首先通过二分法在偏移量索引文件中找到不大于 23 的最大索引项,即[22, 656],然后从日志分段文件中的物理位置 656 开始顺序查找偏移量为 23 的消息。

        

       以上是最简单的一种情况。参考上图,如果要查找偏移量为 268 的消息,那么应该怎么 办呢?首先肯定是定位到baseOffset为251的日志分段,然后计算相对偏移量relativeOffset = 268 - 251 = 17,之后再在对应的索引文件中找到不大于 17 的索引项,最后根据索引项中的 position 定位到具体的日志分段文件位置开始查找目标消息。那么又是如何查找 baseOffset 为 251 的日志分段的呢?这里并不是顺序查找,而是用了跳跃表的结构。Kafka 的每个日志对象中使用了 ConcurrentSkipListMap 来保存各个日志分段,每个日志分段的 baseOffset 作为 key,这样可以根据指定偏移量来快速定位到消息所在的日志分段。

      在Kafka中要定位一条消息,那么首先根据 offset 从 ConcurrentSkipListMap 中来查找到到对应(baseOffset)日志分段的索引文件,然后读取偏移量索引索引文件,之后使用二分法在偏移量索引文件中找到不大于 offset - baseOffset z的最大索引项,接着再读取日志分段文件并且从日志分段文件中顺序查找relativeOffset对应的消息。

      Kafka中通过offset查询消息内容的整个流程我们可以简化成下图:

                          

Kafka为什么不采用InnoDB的索引机制

       InnoDB中维护索引的代价比Kafka中的要高。Kafka中当有新的索引文件建立的时候ConcurrentSkipListMap才会更新,而不是每次有数据写入时就会更新,这块的维护量基本可以忽略,B+树中数据有插入、更新、删除的时候都需要更新索引,还会引来“页分裂”等相对耗时的操作。Kafka中的索引文件也是顺序追加文件的操作,和B+树比起来工作量要小很多。

       说到底还是应用场景不同所决定的。MySQL中需要频繁地执行CRUD的操作,CRUD是MySQL的主要工作内容,而为了支撑这个操作需要使用维护量大很多的B+树去支撑。Kafka中的消息一般都是顺序写入磁盘,再到从磁盘顺序读出(不深入探讨page cache等),他的主要工作内容就是:写入+读取,很少有检索查询的操作,换句话说,检索查询只是Kafka的一个辅助功能,不需要为了这个功能而去花费特别太的代价去维护一个高level的索引。前面也说过,Kafka中的这种方式是在磁盘空间、内存空间、查找时间等多方面之间的一个折中。↵

### Kafka 索引机制Kafka中,为了提高消息检索效率并减少磁盘占用,采用了稀疏索引的设计[^2]。具体而言,每个分区(Partition)下的日志文件被分割成多个段(Segment)。每一段不仅包含实际的消息数据,还维护了两种类型的索引文件:偏移量索引文件和时间戳索引文件。 #### 偏移量索引文件 偏移量索引文件记录了一部分特定位置处的绝对偏移量及其对应的物理地址。由于不是每一个消息都建立索引条目,因此被称为“稀疏”。这种设计使得即使有大量的历史数据也不会造成过多的内存消耗或I/O开销。当消费者请求某个具体的偏移量时,系统会先利用这些预存的位置快速定位到最接近目标但不超过它的那一点,再从此处线性扫描直至找到确切的目标消息[^1]。 #### 时间戳索引文件 除了基于偏移量外,Kafka也允许按照事件发生的时间来访问消息。为此,在相同的原则下创建了时间戳索引文件,它同样遵循稀疏原则只保存某些时刻对应的信息。这有助于实现诸如按时间段查询等功能需求。 #### 相对偏移量的作用与计算方法 相对偏移量指的是相对于当前段内第一条记录的实际偏移值。引入这一概念主要是考虑到随着新消息不断追加至末尾,全局唯一性的绝对偏移可能会变得非常大而难以管理;相比之下,局部范围内的相对编号则更加紧凑易控。每当开启一个新的segment时,其内部所有entry都会重新计数从0开始累加形成各自的relative offset[^4]。 ```python def calculate_relative_offset(global_offset, segment_start_offset): """ 计算给定全局偏移量在全球范围内所指向的具体message在其所属segment中的相对位置 参数: global_offset (int): 要转换为相对偏移量的全局偏移量. segment_start_offset (int): 当前segment起始的全局偏移量. 返回: int: 给定global_offset对应的相对偏移量. """ relative_offset = global_offset - segment_start_offset return relative_offset ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值