🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] # Broker 不同于redis和memcache等内存消息队列,kafka的设计是把所有的message都写入速度低容量大的硬盘,以此换取更强的存储能力.实际上kafka使用硬盘并没有带来过多的性能损失,是抄了一条近道 首先说,kafka在磁盘上只做了Sequence I/O,由于消息系统读写的特殊性,这并不存在什么问题.关于磁盘I/O的性能,引用一组kafka官方给的测试数据(Raid-5,7200rpm): Sequence I/O:600MB/S Random I/O : 100KB/S 所以通过只做Sequence I/O的限制,规避了磁盘访问速度底下对性能可能造成的影响 # PageCache 然后kafka重度依赖操作系统提供的PageCache功能.当上层有写操作时,操作系统只是将数据写入PageCache,同时标记Page属性为Dirty 当读操作发生时,先从PageCache中查找,如果发生缺页才进行磁盘调度,最终返回需要的数据.**实际上PageCache是把尽可能多的空闲内存都当做了磁盘缓存来使用** 同时如果有其他进程申请内存,回收PageCache的代价很小,所以现代OS都支持PageCache 使用PageCache功能同时避免在jvm内部缓存数据,JVM为我们提供了强大的GC能力,同时也引入了一些问题不适用与kafka设计 * 如果在Heap内管理缓存,JVM的GC线程会频繁扫描Heap空间,带来不必要的开销.如果Heap过大,执行一次Full GC对系统的可用性来说将是极大的挑战 * 所有在JVM内的对象都不免带有一个Object Overhead(千万不可小视),内存的有效空间利用率会因此降低 * 所有的In-Process Cache在OS中都有一份同样的PageCache.所以通过将缓存只放在PageCache可以至少让可用缓存空间翻倍 * 如果kafka重启,所有的In-Process Cache都会失效,而OS管理的PageCache依然可以继续使用 PageCache还只是第一步,kafka为了进一步的优化性能,还采用了SendFile. # sendfile 在解释sendfile之前,首先介绍下传统的网络I/O操作流程,大体上分为以下4步 1. OS从硬盘把数据读到内核区的PageCache 2. 用户进程把数据从内核区Copy到用户区 3. 然后用户进程再把数据写入到socket,数据流入内核区的socket Buffer上 4. OS再把数据从Buffer中copy到网卡的Buffer上,这样就完成一次发送 ![](https://box.kancloud.cn/7c21513c0bf745e9aa8c5c44579085ef_686x534.png) 整个过程共经历两次Context Switch,四次System call.同一份数据在内核Buffer与用户Buffer之间重复拷贝,效率低下 其中2,3两步没必要,完全可以在内核区完成数据拷贝.这也正是sendfile所解决的问题. 经过sendfile优化后,整个I/O过程就变成了下面的样子 ![](https://box.kancloud.cn/76d7eba5f600b5da5582b29840c27143_629x534.png) 通过以上的介绍不难看出,kafka的设计初衷是尽一切努力在内存中完成数据交换,无论是对外作为一整个消息系统,或是内部同底层操作系统的交互. 如果Producer和Consumer之间生产和消费进度上配合得当,完全可以实现数据交换零I/O.这也就是我们所说kafka使用硬盘并没有带来过多性能损耗