一、写数据(生产数据-顺序写磁盘)
Kafka 的 producer 生产数据,要写入到 log 文件中,写的过程是一直追加到文件末端,为顺序写。官网有数据表明,同样的磁盘,顺序写能到 600M/s,而随机写只有 100K/s。这与磁盘的机械机构有关,顺序写之所以快,是因为其省去了大量磁头寻址的时间。
二、读数据(消费数据)
Kafka 高吞吐量的其中一个重要功臣就是“零拷贝”。从字面上理解就是数据不需要多次拷贝,系统性能大幅度提升。其实,不仅在kafka中,Java NIO,netty,rocketMQ 等框架中也都用到了零拷贝。
1、传统的文件拷贝
传统的文件拷贝通常需要将拷贝请求从用户态转到核心态,经过核心态的read buffer,然后再返回到用户态的应用层buffer,然后再从用户态把数据拷贝到核心态的socket buffer,最后发送到网卡。如下图所示:
传统的数据传输需要多次的用户态和核心态之间的切换,而且还要把数据复制多次,最终才打到网卡。如果减少了用户态与核心态之间的切换,是不是就会更快了呢?如下图:
用户态“空空如也”。数据没有来到用户态,而是直接在核心态就进行了传输,但这样依然还是有多次复制。首先数据被读取到read buffer中,然后发到socket buffer,最后才发到网卡。虽然减少了用户态和核心态的切换,但依然存在多次数据复制。如果可以进一步减少数据复制的次数,甚至没有数据复制是不是就会做到最快呢?
2、DMA
DMA,全称叫Direct Memory Access(直接内存访问),一种让某些硬件子系统去直接访问系统主内存而不用依赖CPU的计算机系统的功能。传统的内存访问都需要通过CPU的调度来完成,而 DMA 可以跳过CPU,直接访问主内存。如下图:
绕过CPU访问内存:
很多硬件都支持DMA,这其中就包括网卡。
3、零拷贝
有了DMA后,就可以实现零拷贝了,因为网卡是直接去访问系统主内存的。如下图:
在Java中零拷贝的实现在FileChannel中,其中有个方法transferTo(position,fsize,src)。transferTo方法底层是基于操作系统的sendfile这个system call来实现的(不再需要拷贝到用户态了),sendfile负责把数据从某个fd(file descriptor)传输到另一个fd。
Kafka在文件传输的过程中正是使用了零拷贝技术对文件进行拷贝,因此大大提升了文件传输的性能。