kafka是基于磁盘文件读写,使用磁盘而不是kafka服务器broker进程内存来进行数据存储。我们对于计算机的基础认知是IO操作是效率低下的,那么为什么kafka会采用磁盘文件存储的方式效率确还是很快? kafka高效IO的原因,从以下几个方面总结分析。
1.消息批量发送
producer产生消息发送给broker,消息传输需要建立连接。kafka为了减少频繁的建立连接性能开销,不是产生一条消息就立即发送,而是存储到消息缓冲池,当消息缓冲池累计到一定数量进行批量发送。
producer将消息发送到消息缓冲池
RecordAccumulator
,详细过程参见kafka学习笔记(二) Producer分析-基本工作流程 。
2.消息写入分析
2.1)顺序写入
一般来说IO写入效率不高的原因是因为写入磁盘的内容不是顺序的,磁盘写入过程像老式的黑胶唱片一样,由一个写入磁头和磁盘组成。
- 若写入磁盘内容是顺序的,那么磁头不需要变化,只需要磁盘不停转动即可。
- 若写入磁盘内容不是顺序的,那么磁头需要不停的变化位置。
IO写入效率主要的影响因素正是磁头频繁改变。而kafka在每个partition内都是顺序追加写入的。
2.2)页面缓存pagecache
- 虽然消息写入是磁盘顺序写入,没有磁盘寻道的开销,但是如果针对每条消息都执行一次磁盘写入,则也会造成大量的磁盘IO,影响性能。
- 所以在消息写入方面,broker基于MMAP技术,即内存映射文件,将消息先写入到操作系统的页缓存中,由页缓存直接映射到磁盘文件,不需要在用户空间和内核空间直接拷贝消息,所以也可以认为消息传输是发送在内存中的。
- 由于是先将消息写入到操作系统的页缓存,而页缓存数据刷新同步sync到磁盘文件是由操作系统来控制的,即操作系统通过一个内核后台线程,每5秒检查一次是否需要将页缓存数据同步到磁盘文件,如果超过指定时间或者超过指定大小则将页缓存数据同步到磁盘。所以如果在刷新到磁盘文件之前broker机器宕机了,则会导致页缓存的数据丢失。具体可以参考:Kafka消息的存储
- 使用页缓存的另外一个好处是,如果重启了kafka服务端(这个服务重启,而不是机器重启),页缓存中的数据还是可以继续使用的。
利用操作系统自身的内存而不是JVM空间内存。这样做的好处有:
- 避免Object消耗:如果是使用Java堆,Java对象的内存消耗比较大,通常是所存储数据的两倍甚至更多。
- 避免GC问题:随着JVM中数据不断增多,垃圾回收将会变得复杂与缓慢,使用系统缓存就不会存在GC问题。
3.消息读取分析
3.1)”零”拷贝
从消息读取的角度分析。 应用层不能直接与物理层交互,需要借助通过内核。一般读取磁盘文件流程:
传统的IO与socket传输模式
应用要读取磁盘数据,需要调用内核api,操作系统读取磁盘文件,将文件数据读取出来暂存在系统内核存储空间中,然后将文件数据复制给用户态内存空间。发送时需要将用户态内存空间的数据复制给系统内核,由系统内核与网卡交互将数据发送出去。
这样的一读一发,经过多次的IO操作,大大的耗损服务器性能。kafka采用”零”拷贝的方式减少IO频繁的复制。说是”零”,只是将内核态与用户态之间的复制免去了。
基于MMAP的零拷贝
操作系统提供了sendfile系统调用来支持MMAP机制,即应用只需指定需要传输的磁盘文件句柄,然后通过sendfile系统实现磁盘文件读取和从socket传输出去,其中磁盘文件的读取和从socket传输出去都是通过sendfile系统调用在内核完成的,不需要在内核空间和用户空间进行数据拷贝,具体过程如下:
- 应用指定需要传输的文件句柄和调用sendfile系统调用;
- 操作系统在内核读取磁盘文件拷贝到页缓存;
- 操作系统在内核将页缓存内容拷贝到网卡硬件缓存。
整个过程都在内核态存储空间内进行,避免了内核态与用户态之间的数据复制过程。
3.2)稀疏索引
.index 存储稀疏索引 假设刚刚我们定位要消费的偏移量是在 00000000000000368769.log 文件里。 如何根据 index 文件定位呢?
- 首先在 index 文件里找,index 文件存储的数据都是成对出现的,比如我们到的 1,0 代表的意思是,offset=368769+1=368770 这条信息存储的物理位置是 0 这个位置。那现在我们现在想要定位的消息是 368776 这条消息,368776 减去 368769 等于 7,我们就在 index 文件里找 offset 等于 7 对应的物理位置,但是因为是稀松索引,我们没找到,不过我们找到了 offset 等于 6 的物理值 1407。
- 接下来就到 log 文件里读取文件的 1407 的位置,然后遍历后面的 offset,很快就可以遍历到 offset 等于 7(368776)的数据了,然后从这儿开始消费即可