一、kafka存储结构
1.kafka为什么使用磁盘作为存储介质
2.分析文件存储格式
3.快速检索消息
1.kafka存储结构
Kafka 的基本存储单元是分区(partition)
(1)每个partition相当于一个大文件被平均分配到多个大小相等的segment段(数据文件)里,
默认情况下,每个片段包含 1GB 或一周的数据,以较小的那个为准。
(2)每个patition只需要支持顺序读写即可,segment文件的生命周期由服务端配置参数决定。
它将每个 Partition 分为多个 Segment,每个 Segment 对应两个文件:“.index” 索引文件和 “.log” 数据文件。
这些文件位于同一文件下,该文件夹的命名规则为:topic 名-分区号。例如,first 这个 topic 有三分分区,则其对应的文件夹为 first-0,first-1,first-2。
# ls /root/data/kafka/first-0
00000000000000009014.index
00000000000000009014.log
00000000000000009014.timeindex
00000000000000009014.snapshot
leader-epoch-checkpoint
2.partition中segment文件存储结构
(1)segment文件组成:主要由2部分组成,index文件、data文件,这两个文件一一对应,成对出现,分别代表索引文件和数据文件,二者名称一样,仅仅只是后缀不一样,一个是.index,一个是.log
(2)segment文件命名规则:partition中第一个segment从0开始,每个segment文件名称为上一个segment文件最后一条消息的offset值。数值最大为64为long大小,19位数字字符长度,没有数字用0填充。
二、kafka日志索引
1.数据文件的分段
kafka解决查询效率的手段之一是将数据文件分段,比如有100条Message,他们的offset是从0到99的,假如数据分为5段,第一段为0-19,第二段为20-39,以此类推,这样在查找指定offset的Message时,用二分查找就可以定位到该消息再哪个段中。
2.偏移量索引
数据文件分段使得可以快速查到对应的Message,但是依旧需要扫描才能找到对应的Message。为了进一步提供查询效率,kafka为每个分段后的segment文件建立了索引文件,索引文件名称与数据文件名称都是一样的,只是扩展名为.index。
三、kafka日志清理
1.Kafka日志管理器删除策略
(1)按时间删除:目前策略是删除修改时间在N天之前的日志。
(2)按大小删除:保留最后的N GB数据。
2.超时数据的清理机制
一般情况下,Kafka 会根据设置的时间保留数据,把超过时效的旧数据删除掉。
Kafka 通过改变主题的保留策略来满足这些使用场景 ,早于保留时间的事件会被删除, 为每个键保留最新的值, 从而达到清理的效果每个日志片段可以分为以下两个部分 :
-
干净的部分,这些消息之前被清理过, 每个键只有一个对应的值,这个值是上一次清理时保留下来的。
-
污浊的部分,这些消息是在上一次清理之后写入的。
清理分区
为了清理分区, 清理线程会读取分区的污独部分, 并在内存里创建一个 map。 map 里的每个元素包含了消息键的散列值和消息的偏移量,键的散列值是 16B,加上偏移量总共是 24B。 如果要清理一个 1GB 的日志片段,并假设每个消息大小为 1KB,那么这个片段就包含_一百万个消息,而我们只需要用 24MB的 map 就可以清理这个片段。 (如果有重复的键, 可以重用散列项, 从而使用更少的内存。 )
四、kafka日志压缩(日志清理的一种方式)
1.日志压缩
将数据压缩,只是保留每个key最后一个版本的数据。 清理的思想就是根据 Key 的重复来进行整理,注意,它不是数据删除策略,而是类似于压缩策略,如果 key 送入了值,对于业务来说,key 的值应该是最新的 value 才有意义,所以进行清理后只会保存一个 key 的最新的 value,这个适用于一些业务场景,比如说 key 代表用户 ID,Value 用户名称,如果使用清理功能就能够达到最新的用户的名称的消息(这个功能有限,请参考使用)
2.注意
kafka默认是关闭日志压缩的,启用压缩策略,需要分两步配置:
(1)在broker中配置:log.cleaner.enable=true
(2)在Topic中配置:log.cleanup.policy=compact
五、kafka为什么要用磁盘作为存储介质
Kafka选择使用磁盘作为存储介质,主要基于以下几个方面的原因:
1. 顺序读写优化
- 顺序写入速度快:磁盘的顺序读写性能远高于随机读写。Kafka在设计时采用了文件追加的方式来写入消息,即只能在日志文件的尾部追加新的消息,并且不允许修改已写入的消息,这种方式属于典型的顺序写盘操作,能够充分发挥磁盘顺序写入的性能优势。
- 操作系统优化:操作系统可以对顺序读写进行深层次的优化,如预读(read-ahead)和后写(write-behind)技术,进一步提升读写效率。
2. 页缓存和mmap技术
- 页缓存:Kafka大量使用了操作系统的页缓存机制。页缓存将磁盘中的数据缓存到内存中,减少对磁盘I/O的操作。当进程需要读取磁盘上的文件时,如果数据已在页缓存中,则直接返回数据,避免了磁盘I/O。
- mmap(内存映射文件):Kafka通过mmap技术将磁盘文件映射到内存中,用户可以通过修改内存来修改磁盘文件,提高了数据的读写效率。
3. 避免JVM内存和系统内存之间的复制
- 减少性能开销:直接使用磁盘进行存储,避免了数据在JVM内存和系统内存之间的复制,减少了性能的创建对象和垃圾回收的开销。
4. 高效的数据管理和查询
- 数据文件分段:Kafka将topic中的一个partition大文件分成多个小文件段(segment),通过多个小文件段,容易定期清除或删除已经消费完的文件,减少磁盘占用。
- 索引机制:为了提高查询效率,Kafka为每个分段后的数据文件建立了索引文件。索引文件中包含索引条目,每个条目表示数据文件中一条消息的索引,包括相对offset和position,通过索引可以快速定位消息。
5. 可靠性和持久性
- 数据持久化:Kafka将消息持久化到磁盘,即使Kafka服务重启,页缓存还是会保持有效,而进程内的缓存却需要重建。这极大地简化了代码逻辑,并提高了数据的可靠性和持久性。
- 多副本机制:Kafka通过数据的复制机制,确保了在某些节点出现故障的情况下,系统依然能够正常工作,不会丢失数据。
综上所述,Kafka选择使用磁盘作为存储介质,主要是因为磁盘的顺序读写性能优异、页缓存和mmap技术提高了数据读写效率、避免了JVM内存和系统内存之间的复制开销、实现了高效的数据管理和查询机制,以及提供了可靠的数据持久化和多副本机制。这些因素共同使得Kafka成为一个高性能、高可靠性的消息系统。