Kafka零拷贝(四)

Kafka-零拷贝(四)

Kafka依赖于文件系统(磁盘)来存储和缓存信息.对于Kafka的零拷贝,我们先来了解下磁盘存储.

磁盘存储



上图可以看出各种存储介质的存取速度.

操作系统为了减少磁盘的读写速度与主存的差距,做了很多优化,比如:预读:提前讲一个比较大的磁盘块读入内存;后写:将很多小的逻辑写操作合并起来组成一个大的物理写操作技术.顺序写磁盘的速度不仅比随机写盘的速度快,而且比随机写内存的速度快.



Kafka在设计时采用了文件追加的方式来写消息,即只能在日志文件的尾部追加新的消息,并且也不允许修改已写入的消息.这种方式属于典型的顺序写磁盘操作,所以就算Kafka使用磁盘作为存储介质,它的性能也是非常好的.

页缓存

页缓存是操作系统实现的一种主要的磁盘缓存,以此用来减少对磁盘 I/0 的操作。具体来说, 就是把磁盘中的数据缓存到内存中, 把对磁盘的访问变为对内存的访问。为了弥补性能上的差异, 现代操作系统越来越 “激进地”将内存作为磁盘缓存,甚至会非常乐意将所有可用的内存用作磁盘缓存, 这样当内存回收时也几乎没有性能损失, 所有对于磁盘的读写也将经由统一的缓存.

当一个进程准备读取磁盘上的文件内容时, 操作系统会先查看待读取的数据所在的页(page)是否在页缓存(pagecache)中,如果存在(命中)则直接返回数据, 从而避免了对物理磁盘的IO操作;如果没有命中 则操作系统会向磁盘发起读取请求并将读取的数据页存入页缓存, 之后再将数据返回给进程, 同样, 如果一个进程需要将数据写入磁盘,那么操作系统也会检测数据对应的页是否在页缓存中,如果不存在, 则会先在页缓存中添加相应的页,最后将数据写入对应的页 被修改过后的页也就变成了脏页,操作系统会在合适的时间把脏页中的数据写入磁盘,以保持数据的致性.

Linux 操作系统中的vm.dirty_bacxground_ratio参数用来指定当脏页数量达到系统内存的百分之多少之后就会触发 pdflush/flush/kdmflush等后台回写进程的运行来处理脏页,一般设置为小于 10的值即可,但不建议设置为0.与这个参数对应的还有个vm.dirty_ratio参数, 它用来指定当脏页数量达到系统内存的百分之多少之后就不得不开始对脏页进行处理,在此过程中, 新的 I/O请求会被阻挡直至所有脏页被冲刷到磁盘中。对脏页有兴趣的读者还可以自行查阅 vm.dirty_expire_centisecs, vm.adirty_writeback_centisecs等参数的使用说明.

对一个进程而言,它会在进程内部缓存处理所需的数据,然而这些数据有可能还缓存在操作系统的页缓存中,因此同一份数据有可能被缓存了两次并且 ,除非使用DirectI/0的方式,否则页缓存很难被禁止 此外 用过Java的人一般都知道两点事实: 对象的内存开销非常大,通常会是真实数据大小的几倍甚至更多, 空间使用率低下;Java的垃圾回收会随着堆内数据的增多而变得越来越慢.基于这些因素, 使用文件系统并依赖于页缓存的做法明显要优于维护一个进程内缓存或其他结构, 至少我们可以省去了一份进程内部的缓存消耗,同时还可以通过结构紧凑的字节码来替代使用对象的方式以节省更多的空间。如此,我们可以在32GB 的机器上使用28GB至30GB的内存而不用担心GC所带来的性能问题。此外,即使Kafka 服务重启,页缓存还是会保持有效,然而进程内的缓存却需要重建.这样也极大地简化了代码逻辑,因为维护页缓存和文件之间的一致性交由操作系统来负责,这样会比进程内维护更加安全有效

Kafka中大量使用了页缓存,这是Kafka实现高吞吐量的重要因素之一.虽然消息都是被先下入页缓存的,然后由操作系统负责具体的刷盘任务,但在Kafka中同样提供了同步刷盘及间断性强制刷盘的功能.log.flush.interval.message,log.flush.interval.ms等参数来控制.同步刷盘可以提高消息的可靠性,防止由于机器掉电等异常造成处于页缓存而没有技术刷入磁盘的消息丢失.一般来说,这里不需要我们去控制,交给操作系统去控制就好了,既能提高效率,又减少不必要的操作.消息可靠性可以交给Kafka本身的副本去保证.

零拷贝

前面说了些磁盘,页缓存,Kafka还使用了零拷贝技术来进一步提升性能.所谓的零拷贝是指将数据直接从磁盘文件复制到网卡设备中,而不需要经由应用程序之手,零拷贝大大提高了应用程序的性能,减少了内核和用户模式之间的上下文切换.

正常的读取写入数据流程:


1)调用read()时,文件A中的内容被复制到内核模式下的Read Buffer中.

2)CPU控制将内核模式数据复制到用户模式下.

3)调用write()时,将用户模式下的内容复制到内核模式下的Socket Buffer中.

4)将内核模式下的Socket Buffer的数据复制到网卡设备中传送.

从上面可以看出,从内核模式到用户模式,复制了4次.还有用户模式和内核的上下文切换4次

采用零拷贝技术:



零拷贝技术通过DMA(direct memory access)技术将文件内容复制到内核模式下的Read Buffer中,不过没有数据被复制到Socket Buffer,只有包含数据的位置和长度的信息的文件描述符被加到了socket buffer中.DMA引擎直接将数据从内核模式中传递到网卡设备.这里数据只经历了2次复制就直接从磁盘发送到网络中去了.零拷贝是针对内核模式而言的,数据在内核模式下实现了零拷贝.

总结就是: Kafka不直接使用内存来做存储,但是性能依然很快的原因,因为使用了零拷贝,磁盘顺序写。

2019-12-04 12:1192