《Kafka 高性能 7 大秘诀》第 4 篇,解密 kafka Segment 日志存储思想哲学以及如何将磁盘的随机读写变成顺序读写,提高磁盘读写速度。
Kafka 使用日志文件存储消息,每个 Partition 的消息被存储在多个 Segment 文件中,避免了单个文件过大的问题,每个 Segment 文件由一组连续的消息组成。
为了加快消息的检索,每个 Segment 除了实际的数据日志文件外(.log
后缀)之外,还有两个索引文件。
偏移量索引文件
.index
时间戳索引文件
.timeindex
。
索引文件使用稀疏索引,通过 mmap 映射到内存中,减少了磁盘 IO 操作。
这样,Kafka 可以快速定位消息,提升读取性能。同时,顺序写入的方式使得磁盘写操作更加高效,减少了寻道时间和旋转延迟。
例如,当 Producer 写入消息时,Kafka 将消息顺序追加到当前的 Segment 文件末尾,避免了磁盘的随机写入,从而大幅提升了写入性能。
顺序读写文件
★码楼:“不管如何,Kafka 读写消息都要读写磁盘,如何变快呢?”
磁盘就一定很慢么?人们普遍错误地认为硬盘很慢。然而,存储介质的性能,很大程度上依赖于数据被访问的模式。同样在一块普通的 7200 RPM SATA 硬盘上,随机 I/O(random I/O)与顺序 I/O 相比,随机 I/O 的性能要比顺序 I/O 慢 3 到 4 个数量级。
合理的方式可以让磁盘写操作更加高效,减少了寻道时间和旋转延迟。
码楼,你还留着课本吗?来,翻到讲磁盘的章节,让我们回顾一下磁盘的运行原理。
★码楼:“鬼还留着哦,课程还没上到一半书就没了。要不是考试俺眼神好,就挂科了。”
磁盘的运行原理如图 1 所示。
图 1
硬盘在逻辑上被划分为磁道、柱面以及扇区。硬盘的每个盘片的每个面都有一个读写磁头。
完成一次磁盘 I/O ,需要经过寻道
、旋转
和数据传输
三个步骤。
寻道:首先必须找到柱面,即磁头需要移动到相应磁道,这个过程叫做寻道,所耗费时间叫做寻道时间。寻道时间越短,I/O 操作越快,目前磁盘的平均寻道时间一般在 3-15ms。
旋转:磁盘旋转将目标扇区旋转到磁头下。这个过程耗费的时间叫做旋转时间。旋转延迟取决于磁盘转速,通常用磁盘旋转一周所需时间的 1/2 表示。比如:7200rpm 的磁盘平均旋转延迟大约为 60*1000/7200/2 = 4.17ms,而转速为 15000rpm 的磁盘其平均旋转延迟为 2ms。
数据传输:数据在磁盘与内存之间的实际传输。
因此,如果在写磁盘的时候省去寻道
、旋转
可以极大地提高磁盘读写的性能。
Kafka 采用顺序写
文件的方式来提高磁盘写入性能。顺序写
文件,顺序 I/O 的时候,磁头几乎不用换道,或者换道的时间很短。减少了磁盘寻道
和旋转
的次数。磁头再也不用在磁道上乱舞了,而是一路向前飞速前行。
Kafka 中每个 Partition 是一个有序的,不可变的消息序列,新的消息可以不断追加到 Partition 的末尾,在 Kafka 中 Partition 只是一个逻辑概念,每个 Partition 划分为多个 Segment,每个 Segment 对应一个物理文件,Kafka 对 Segment 文件追加写,这就是顺序写文件。
例如,当 Producer 写入消息时,Kafka 将消息顺序追加到当前的 Segment 文件末尾,避免了磁盘的随机写入,从而大幅提升了写入性能。
Segment 日志文件管理
前面已经介绍过,Kafka 的 Topic 可以分为多个 Partition,每个 Partition 有多个副本,你可以理解为副本才是存储消息的物理存在。其实每个副本都是以日志(Log)的形式存储。
★码楼:“日志文件过大怎么办?”
为了解决单一日志文件过大的问题,kafka 采用了分段(Segment)的形式进行存储。
所谓 Segment,就是当一个日志文件大小到达一定条件之后,就新建一个新的 Segment,然后在新的 Segment 写入数据。Topic、Partition、和日志的关系如图 2 所示。