kafka_1

kafka的几个重要概念

  • Broker:消息中间件处理节点,一个 Kafka 节点就是一个 broker,多个 broker 可以组成一个 Kafka 集群;

  • Topic:一类消息,例如 note impression 日志、 click 日志等都可以以 topic 的形式存在,Kafka 集群能够同时负责多个 topic 的分发;

  • Partition:topic 物理上的分组,一个 topic 可以分为多个 partition,每个 partition 是一个有序的队;

  • segment:每个 partition 又由多个segment file组成;

  • offset:每个 partition 都由一系列有序的、不可变的消息组成,这些消息被连续的追加到 partition 中。partition 中的每个消息都有一个连续的序列号叫做 offset,用于 partition 唯一标识一条消息;

  • message:这个算是 kafka 文件中最小的存储单位,即是 one commit log。

Partition中的文件

segment file

对于一个 partition,里面又有很多大小相等的 segment 数据文件(这个文件的具体大小可以在config/server.properties中进行设置),这种特性可以方便 old segment file的快速删除。

  1. segment的索引文件中存储着大量的元数据,数据文件中存储着大量消息,索引文件中的元数据指向对应数据文件中的 message 的物理偏移地址。以索引文件中的6,1407为例,在数据文件中表示第6个 message(在全局 partition 表示第368775个 message),以及该消息的物理偏移地址为1407。

:.index文件的第一个数是 message 相对 offset,后面的数字是该消息的物理偏移地址。

为什么分成多个segment file

  1. 问题: 如果 topic 的 partition 中只有一个文件会怎么样?

  2. 新数据加在文件的末尾(调用内部方法),不论文件多大,该操作的时间复杂度都是O(1);这个没问题。

  3. 查找某个 offset 的时候,是顺序查找。想想,如果文件很大的话,查找的效率就会很低;这个是要解决的。

  4. Kakfa 是如何解决上述问题的呢 通过上述分析发现,如果 Kafka 只有一个文件的话,插入新数据的效率是没问题的;只是在查找的时候,效率很低。

解决办法

方法一 数据文件分段

将大文件分成多个小的文件,便于维护。 由 offset 的起始位置来命名,则通过二分查找可以确定一个待查找的 offset 属于在那个小文件里面。

方法二 为数据文件建立索引文件

即为上图中可以看到的 .index 文件, 数据分段虽然解决了一个大文件的问题,但是找到具体的小文件后,还是要通过 顺序扫描的方式才能找到对应 offset 的 message。 为了提高顺序扫描的效率,kafka 为每个分段后的文件建立了一个 与数据文件名一样的,扩展名为 .index 的文件。

索引文件的内部结构

索引文件包含两个部分(均为4个字节),分别为 offset 和 position。

  • 相对 offset: 由于数据文件分段以后,除了第一个数据以外,每个数据文件的起始 offset 不为0,相对 offset 表示 该 message 相对于其所属数据文件中最小的 offset 的大小,即在每个文件中都是从1开始。 然后查找具体 offset 的时候, 只需要通过二分查找找到具体的在哪个文件中,然后再用 offset - 文件名对应的数值, 即可确定在文件中的相对 offset。 存储相对 offset 的好处是可以减小索引文件占用的空间

  • position: 表示该条 message 在数据文件中的绝对位置, 只要打开数据文件并移动文件指针到这个 position 就可以 读取到对应的 message 数据了。

  • 通过 offset 查找 message 分为两个步骤:

  • 通过二分查找文件列表,快速定位到具体的.index和.log文件; 即找到小于或等于给定 offset 的最大的 offset 文件。

  • 通过 segment file 查找具体的 message,找到具体的文件后,先定位到 .index 文件中的 元数据物理位置 position, 即 .log 文件中的物理偏移地址。 然后在通过顺序查找到具体的 offset。

上述最后还需要顺序查找具体的 offset, 而不是直接通过 position 定位到具体的 message,是因为 segment index file 没有为数据文件中的每条 message 建立索引,而是采用稀疏索引的存储方式,每隔一定字节的数据建立一条索引,这样可以减小 索引文件,通过 map 可以直接进行内存操作, 稀疏索引为数据文件的每个对应 message 设置一个元数据指针, 这样比稠密索引 节省了存储空间,但是查找时需要消耗更多的时间,其实最后通过 .index 文件中 position 无法直接定位到 没有建立索引 的 message, 而是需要通过顺序查找,再继续在 .log 文件中继续往下查找。

总结

Kafka 高效文件存储设计特点:

  • 将 topic 中一个 partition 大文件分割成多个小文件段,这样更容易定期清除和删除已经消费完的文件,减少磁盘占用。

  • 通过索引可以快速定位到 message 和 确定 response 的最大大小。

  • 通过 index 元数据全部映射到 memory,可以避免 segment file的 IO 磁盘操作。

  • 通过索引文件稀疏存储,可以大幅降低 index 文件元数据占用空间大小。

参考

Last updated