1. Kafka集群用log和index文件来存储消息
例如00000.log里存放的就是从offset 0开始的消息,相应地,00000.index里存放的是特定offset的消息在00000.log文件中的地址。
而当00000.log文件大小达到某个特定值时,就会写入下一个文件,00078.log以及00078.index。里面存放的是最小位移为78的消息。
另外,索引采用的是稀疏索引,并没有对每一个offset都建立索引。
$ ls -l
total 40
-rw-r--r-- 1 user admin 0 3 27 15:09 00000000000000000000.index
-rw-r--r-- 1 user admin 224 3 27 15:09 00000000000000000000.log
-rw-r--r-- 1 user admin 12 3 27 15:09 00000000000000000000.timeindex
-rw-r--r-- 1 user admin 10485760 4 3 15:19 00000000000000000078.index
-rw-r--r-- 1 user admin 107 4 3 15:19 00000000000000000078.log
-rw-r--r-- 1 user admin 10 4 3 15:19 00000000000000000078.snapshot
-rw-r--r-- 1 user admin 10485756 4 3 15:19 00000000000000000078.timeindex
log文件大小是由kafka segment参数决定的。默认是1个G.
# The maximum size of a log segment file. When this size is reached a new log segment will be created.
log.segment.bytes=1073741824
2. Kafka写文件的方式(Kafka吞吐量大的部分原因)
2.1 页缓存
操作系统中,有机制将磁盘中的数据放入内存中,也就是页缓存。进程对页缓存读写,操作系统负责页缓存和磁盘之间的数据同步。
Kafka中大量使用了页缓存,大大提高了系统的吞吐量。
2.2 顺序写
Kafka写文件采用了追加的方式,顺序写入磁盘的。
磁盘内部写入的时候,分为随机写入,和顺序写入。随机写入需要磁盘的磁头不断变道,寻址,再进行写操作。变道寻址会占用不少时间。而顺序写入,基本上不用变道和寻址,省去了很多时间。
所以Kafka写磁盘效率很高。
2.3 零拷贝 (zero-copy)
另外,还采用了零拷贝技术。零拷贝指的是减少数据拷贝的次数。
主要体现在 接收生产者发送的数据,和 取出消息发送给消费者时。
- 传统方式中,接收消息,存入磁盘,会发生几次数据拷贝:
首先,从网盘拷贝到socket;
再从内核态的socket拷贝到用户态的应用内存;
接着从用户态的内存拷贝到内核态的read buffer;
最后从read buffer拷贝到硬盘。
这个过程发生了4次数据拷贝,2次用户态和内核态之间的切换,都是开销很大的。
- 在kafka中,通过零拷贝,也就是直接在内核态,将数据直接从网卡传输到磁盘,不经过应用程序,也不用切换到用户态。大大提高了性能。
注:操作系统有一些核心的功能,涉及到底层的很多关键操作,是不能随便让哪个程序就能去做的。那么需要做这部分操作,就需要到内核态执行。
一般情况下,应用程序都是在用户态执行,它只需要一些普通的权限就可以了。当应用程序需要执行一些底层的操作时,就需要先切换到内核态,才有权限去做。
而切换时,需要通过很多操作来完成,例如堆栈的切换等,开销是比较大的。(用户态与内核态)
3. Kafka的日志和index是什么时候写入的
Kafka的日志并不是实时写入磁盘的。消息先被写入页缓存,待操作系统统一进行刷盘,将页缓存写入磁盘。
另外,kafka也提供了强制间隔刷盘,以及同步刷盘的选项,可以通过参数来指定。但是同步刷盘并不推荐,因为它严重影响了性能。
那么间隔刷盘,消息会不会丢呢?kafka的多副本机制是可靠性的保证,那么单个replication如果突发断电等,数据丢失,有其他副本依然保存了数据。所以间隔刷盘,消息可靠性依然是有保证的。
4. 读取某个offset的消息
在kafka中,topic的某个partition的每个log文件作为log segment,是通过跳表来存储的。
当需要读取某个offset的消息时,先通过跳表来找到相应的log segment。通过index文件找到相应offset的地址,再去log segment中找到相应地址的消息。
5. Kafka集群中的log是怎样被清理的
log清理有两种机制,第一种是日志删除,一种是日志压缩。
5.1 日志删除
按一定的保留策略,直接删除某些log segment。
- 时间
segment所存储消息的时间大于特定值,则需要被删除。 - 大小
log segment的总大小大于特定值,则删除前面的segment。 - offset
若segment的下一个segment 偏移量小于起始偏移量,则删除此segment。
kafka有一个专门负责删除日志的任务,会周期性地扫描segment。符合删除条件的话,就将其标为 .deleted后缀,最后通过一个延迟任务统一删除。这个延迟时间可以通过参数来指定。
5.2 日志压缩
对于相同key的消息,只保留其最新消息。
**********
理解了kafka的消息存储,就了解kafka为什么吞吐量大了
- 消息格式本身经过了合理的设计,用到一些机制,例如存相对offset的方式,来减小消息长度。
- 虽然用磁盘存储,但是用到了页缓存、顺序写磁盘、零拷贝的技术,大大提高了读写速度。
- 有数据文件,和相应的索引文件,读取消息的效率也非常之高。