三. Kafka 高效文件存储设计
- Kafka 把 topic 中一个 parition 大文件分成多个小文件段,通过多个小文件段,就容易定期清除或删除已经消费完文件,减少磁盘占用。
- 通过索引信息可以快速定位 message 和确定 response 的最大大小。
- 通过 index 元数据全部映射到内存,可以避免 segment 文件的 IO 磁盘操作。
- 通过索引文件稀疏存储,可以大幅降低 index 文件元数据占用空间大小。
3.1 kafka 文件存储基本结构
在 Kafka 文件存储中,同一个 Topic 下有多个不同 partition,每个 partition 为一个目录。partition命名规则为Topic名称 + 有序序号。如果 partition 数量为 num,则第一个 partition 序号从 0 开始,序号最大值为 num - 1。
例如,自己创建一个名为 orderMq 的 Topic:
[root@mini1 bin]# ./kafka-topics.sh --create --zookeeper mini1:2181 --replication-factor 2 --partitions 3 --topic orderMq
Created topic "orderMq".
orderMq 这个 topic 对应的 partitions 在三台机器上名称分别为:
drwxr-xr-x. 2 root root 4096 11月 21 22:25 orderMq-0
drwxr-xr-x. 2 root root 4096 11月 21 22:25 orderMq-2
drwxr-xr-x. 2 root root 4096 11月 14 18:45 orderMq-1
drwxr-xr-x. 2 root root 4096 11月 14 18:45 orderMq-2
drwxr-xr-x. 2 root root 4096 11月 21 22:25 orderMq-0
drwxr-xr-x. 2 root root 4096 11月 21 22:25 orderMq-1
注:重复的是副本,partition 名分别为 orderMq-0, orderMq-1, orderMq-2;
每个 partition(即每个目录)相当于一个巨型文件被平均分配到多个大小相等的 **segment(段)**数据文件中,但每个 segment 消息数量不一定相等。这种特性方便旧 segment 文件快速被删除,默认保留7天的数据。例如在 orderMq-0 目录下:
[root@mini3 orderMq-0]# ll
-rw-r--r--. 1 root root 10485760 11月 21 22:31 00000000000000000000.index
-rw-r--r--. 1 root root 219 11月 22 05:22 00000000000000000000.log
由上可知,index 和 log 为后缀名的文件的合称,就是 segment 文件。每个 partition 只需要支持顺序读写就行了,segment 文件生命周期(什么时候创建,什么时候删除)由服务端配置参数决定。
3.2 segment 文件
Segment 文件由两大部分组成,分别为索引文件 (index file) 和数据文件 (data file),这两个文件一一对应,成对出现。如下图所示:
Segment 文件命名规则:partition 全局的第一个 segment 从 0 开始,后续每个 segment 文件名为上一个 segment 文件最后一条消息的 offset 值。数值最大为 64 位 long 大小,19 位数字字符长度,没有数字用 0 填充。
索引文件存储大量元数据,数据文件存储大量消息。**索引文件中元数据指向对应数据文件中message的物理偏移地址。**如下图所示:
上述图中 index 文件存储大量元数据,log 文件存储大量消息,index 文件中元数据指向对应 log 文件中消息的物理偏移地址。其中以 index 文件中元数据3, 497
为例,依次在数据文件中表示第 3 个消息(当前 Segment 的第 3 个,全局 partition 表示第 368769 + 3 = 368772 个消息),以及该消息在对应 log 文件中的物理偏移地址为 497。
3.3 kafka 查找消息
读取 offset=368776 的消息,需要通过下面两个步骤查找。
- 第一步:查找segment file
- 以起始偏移量命名并排序这些文件,只要根据offset 二分查找文件列表,就可以快速定位到具体文件。
- 00000000000000000000.index 表示最开始的文件,起始偏移量 (offset) 为 0
- 00000000000000368769.index 的消息量起始偏移量为 368770 = 368769 + 1
- 00000000000000737337.index 的起始偏移量为 737338=737337 + 1
- 其他后续文件依次类推。最后 offset=368776 时定位到 00000000000000368769.index 和对应log文件。
- 第二步:通过 Segment 查找消息
- 当 offset=368776 时,依次定位到 00000000000000368769.index 的元数据物理位置和 00000000000000368769.log 的物理偏移地址,然后再通过 00000000000000368769.log 顺序查找直到 offset=368776 为止。