共计 4167 个字符,预计需要花费 11 分钟才能阅读完成。
本文次要讲述以下两局部内容:
kafka 数据的存储形式;
kafka 如何通过 offset 查找 message。
1. 前言
写介绍 kafka 的几个重要概念(能够参考之前的博文 Kafka 的简略介绍):
Broker:消息中间件解决结点,一个 Kafka 节点就是一个 broker,多个 broker 能够组成一个 Kafka 集群;
Topic:一类音讯,例如 page view 日志、click 日志等都能够以 topic 的模式存在,Kafka 集群可能同时负责多个 topic 的散发;
Partition:topic 物理上的分组,一个 topic 能够分为多个 partition,每个 partition 是一个有序的队;
Segment:每个 partition 又由多个 segment file 组成;
offset:每个 partition 都由一系列有序的、不可变的音讯组成,这些音讯被间断的追加到 partition 中。partition 中的每个音讯都有一个间断的序列号叫做 offset,用于 partition 惟一标识一条音讯;
message:这个算是 kafka 文件中最小的存储单位,即是 a commit log。
kafka 的 message 是以 topic 为根本单位,不同 topic 之间是互相独立的。每个 topic 又可分为几个不同的 partition,每个 partition 存储一部的分 message。topic 与 partition 的关系如下:
topic
其中,partition 是以文件夹的模式存储在具体 Broker 本机上。
2.partition 中的数据文件
有了下面的介绍,上面咱们开始介绍 Topic 中 partition 的数据文件类型。
2.1.segment 中的文件
对于一个 partition(在 Broker 中以文件夹的模式存在),外面又有很多大小相等的 segment 数据文件(这个文件的具体大小能够在 config/server.properties 中进行设置),这种个性能够不便 old segment file 的疾速删除。
上面先介绍一下 partition 中的 segment file 的组成:
segment file 组成:由 2 局部组成,别离为 index file 和 data file,这两个文件是一一对应的,后缀”.index”和”.log”别离示意索引文件和数据文件;
segment file 命名规定:partition 的第一个 segment 从 0 开始,后续每个 segment 文件名为上一个 segment 文件最初一条音讯的 offset,ofsset 的数值最大为 64 位(long 类型),20 位数字字符长度,没有数字用 0 填充。如下图所示:
segment
对于 segment file 中 index 与 data file 对应关系图,这里咱们选用网上的一个图片,如下所示:
index
segment 的索引文件中存储着大量的元数据,数据文件中存储着大量音讯,索引文件中的元数据指向对应数据文件中的 message 的物理偏移地址。以索引文件中的 3,497 为例,在数据文件中示意第 3 个 message(在全局 partition 示意第 368772 个 message),以及该音讯的物理偏移地址为 497。
注:Partition 中的每条 message 由 offset 来示意它在这个 partition 中的偏移量,这个 offset 并不是该 Message 在 partition 中理论存储地位,而是逻辑上的一个值(如下面的 3),但它却惟一确定了 partition 中的一条 Message(能够认为 offset 是 partition 中 Message 的 id)。
2.2.message 文件
message 中的物理构造为:
message
2.3. 数据文件的外部实现办法
Partition 数据文件蕴含了若干上述格局的 message,依照 offset 由小到大排列在一起,它实现的类是 FileMessageSet,类图如下:
filemessageset
它的次要办法如下:
append: 把给定的 ByteBufferMessageSet 中的 Message 写入到这个数据文件中。
searchFor: 从指定的 startingPosition 开始搜寻,找到第一个 Message 判断其 offset 是大于或者等于指定的 offset,并返回其在文件中的地位 Position。它的实现形式是从 startingPosition 开始读取 12 个字节,别离是以后 MessageSet 的 offset 和 size。如果以后 offset 小于指定的 offset,那么将 position 向后挪动 LogOverHead+MessageSize(其中 LogOverHead 为 offset+messagesize,为 12 个字节)。
read:精确名字应该是 slice,它截取其中一部分返回一个新的 FileMessageSet。它不保障截取的地位数据的完整性。
sizeInBytes: 示意这个 FileMessageSet 占有了多少字节的空间。
truncateTo: 把这个文件截断,这个办法不保障截断地位的 Message 的完整性。
readInto: 从指定的绝对地位开始把文件的内容读取到对应的 ByteBuffer 中。
3. 查找
3.1. 遇到的问题
咱们首先试想一下,如果对于 Kafka 的一个 topic 而言,如果 topic 的 partition 中只有一个数据文件的话会怎么样?
新数据是增加在文件开端(调用 FileMessageSet 的 append 办法),不管文件数据文件有多大,这个操作永远都是 O(1)的。
查找某个 offset 的 Message(调用 FileMessageSet 的 searchFor 办法)是程序查找的。因而,如果数据文件很大的话,查找的效率就低。
3.2. 如何去解决这个问题
由上述咱们晓得大数据培训,如果在 topic 的 partition 中只有一个数据文件的话,Kafka 插入的效率尽管很高,然而查找的效率非常低,那么 Kafka 在外部是如何解决查找效率的的问题呢?对于这个问题,Kafka 有两大法宝:分段和索引。
数据文件的分段
这个是比拟好了解的,退出有 100 条 message,它们的 offset 是从 0 到 99,假如将数据文件分为 5 端,第一段为 0 -19,第二段为 20-39,顺次类推,每段放在一个独自的数据文件外面,数据文件以该段中最小的 offset 命名。这样在查找指定 offset 的 Message 的时候,用二分查找就能够定位到该 Message 在哪个段中。
为数据文件建索引
数据文件分段使得能够在一个较小的数据文件中查找对应 offset 的 message 了,然而这仍然须要程序扫描能力找到对应 offset 的 message。为了进一步提高查找的效率,Kafka 为每个分段后的数据文件建设了索引文件,文件名与数据文件的名字是一样的,只是文件扩大名为.index。
索引文件中蕴含若干个索引条目,每个条目示意数据文件中一条 message 的索引。索引蕴含两个局部(均为 4 个字节的数字),别离为绝对 offset 和 position。
绝对 offset:因为数据文件分段当前,每个数据文件的起始 offset 不为 0,绝对 offset 示意这条 message 绝对于其所属数据文件中最小的 offset 的大小。举例,分段后的一个数据文件的 offset 是从 20 开始,那么 offset 为 25 的 message 在 index 文件中的绝对 offset 就是 25-20 = 5。存储绝对 offset 能够减小索引文件占用的空间。
position:示意该条 message 在数据文件中的相对地位。只有关上文件并挪动文件指针到这个 position 就能够读取对应的 message 了。
在 kafka 中,索引文件的实现类为 OffsetIndex,它的类图如下:
offsetindex
次要的办法有:
append 办法:增加一对 offset 和 position 到 index 文件中,这里的 offset 将会被转成绝对的 offset。
lookup:用二分查找的形式去查找小于或等于给定 offset 的最大的那个 offset
3.3. 通过 offset 查找 message
如果咱们想要读取 offset=368776 的 message(见后面的第三个图),须要通过上面 2 个步骤查找。
查找 segment file
00000000000000000000.index 示意最开始的文件,起始偏移量 (offset) 为 0. 第二个文件 00000000000000368769.index 的音讯量起始偏移量为 368770 = 368769 + 1. 同样,第三个文件 00000000000000737337.index 的起始偏移量为 737338=737337 + 1,其余后续文件顺次类推,以起始偏移量命名并排序这些文件,只有依据 offset 二分查找文件列表,就能够疾速定位到具体文件。
当 offset=368776 时定位到 00000000000000368769.index|log
通过 segment file 查找 message
通过第一步定位到 segment file,当 offset=368776 时,顺次定位到 00000000000000368769.index 的元数据物理地位和 00000000000000368769.log 的物理偏移地址,而后再通过 00000000000000368769.log 程序查找直到 offset=368776 为止。
segment index file 并没有为数据文件中的每条 message 建设索引,而是采取稠密索引存储形式,每隔肯定字节的数据建设一条索引,它缩小了索引文件大小,通过 map 能够间接内存操作,稠密索引为数据文件的每个对应 message 设置一个元数据指针, 它比浓密索引节俭了更多的存储空间,但查找起来须要耗费更多的工夫。
总结:
Kafka 高效文件存储设计特点:
Kafka 把 topic 中一个 parition 大文件分成多个小文件段,通过多个小文件段,就容易定期革除或删除曾经生产完文件,缩小磁盘占用。
通过索引信息能够疾速定位 message 和确定 response 的最大大小。
通过 index 元数据全副映射到 memory,能够防止 segment file 的 IO 磁盘操作。
通过索引文件稠密存储,能够大幅升高 index 文件元数据占用空间大小。