kafka_1
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。
kafka 的 message 是以 topic 为基本单位,不同 topic 之间是相互独立的。 每个 topic 又可分为几个不同的 partition,每个 partition 存储一部分 message。 同一个 topic 的不同 partition 可以分布在不同的 broker 上,这正是分布式的设计。 partition 是以文件夹的形式存储在 broker 机器上。 topic 与 partition 的关系如下:

对于一个 partition,里面又有很多大小相等的 segment 数据文件(这个文件的具体大小可以在config/server.properties中进行设置),这种特性可以方便 old segment file的快速删除。
下面介绍一下 partition 中的 segment file的组成: 1. segment file 组成:由2部分组成,分别为 index file和 data file,这两个文件是一一对应的,后缀”.index”和”.log”分别表示索引文件和数据文件;数值最大为64位 long 大小,19位数字字符长度,没有数字用0填充; 2. 自0.10.0.1开始的kafka segment file组成:多了一部分,还有 .timeindex 索引文件,基于时间的索引文件;目前支持的时间戳类型有两种: CreateTime 和 LogAppendTime 前者表示 producer 创建这条消息的时间;后者表示 broker 接收到这条消息的时间(严格来说,是 leader broker 将这条消息写入到 log 的时间) 3. segment file 命名规则:partition 的第一个 segment 从0开始,后续每个 segment 文件名为上一个 segment 文件最后一条消息的 offset, ofsset 的数值最大为64位(long 类型),20位数字字符长度,没有数字用0填充。如下图所示: 老版本 segment file 结构:
新版本 segment file 结构:


- 1.关于 segment file 中 index 索引文件与 data 文件对应关系图,这里我们借用网上的一个图片,如下所示:
- 2.segment的索引 文件中存储着大量的元数据,数据文件中存储着大量消息,索引文件中的元数据指向对应数据文件中的 message 的物理偏移地址。以索引文件中的6,1407为例,在数据文件中表示第6个 message(在全局 partition 表示第368775个 message),以及该消息的物理偏移地址为1407。
- 3.Kafka message 结构如下图:
注
:.index文件的第一个数是 message 相对 offset,后面的数字是该消息的物理偏移地址。- 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 modified 1yr ago