Kafka 为什么那么快
kafka初衷 企业统一,高通量,低延迟。
Kafka 是一种高吞吐量的分布式发布订阅消息系统,有如下特性:
通过O(1)的磁盘数据结构提供消息的持久化,这种结构对于即使数以TB的消息存储也能够保持长时间的稳定性能。
高吞吐量:即使是非常普通的硬件Kafka也可以支持每秒数百万 [2] 的消息。
支持通过Kafka服务器和消费机集群来分区消息。
支持Hadoop并行数据加载
生产快
producer ——> broker
分布式broker
存储快
broker ——>disk
简单说就是通过buffer控制数据的发送,有时间阀值与消息数量阀值来控制
不同于 Redis 和 MemcacheQ 等内存消息队列,Kafka 的设计是把所有的 Message 都要写入速度低、容量大的硬盘,以此来换取更强的存储能力。实际上,Kafka 使用硬盘并没有带来过多的性能损失,“规规矩矩”的抄了一条“近道”。
首先,说“规规矩矩”是因为 Kafka 在磁盘上只做 Sequence I/O (顺序写),由于消息系统读写的特殊性,这并不存在什么问题。关于磁盘 I/O 的性能,引用一组 Kafka 官方给出的测试数据(Raid-5,7200rpm):
Sequence I/O: 600MB/s (实验室)
Sequence I/O: 400MB-500MB/s (工作场景)
Random I/O: 100KB/s
1
2
3
所以通过只做 Sequence I/O 的限制,规避了磁盘访问速度低下对性能可能造成的影响。
接下来我们再聊一聊 Kafka 是如何“抄近道的”。
首先,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 之前,首先介绍一下传统的网络 I/O 操作流程,大体上分为以下 4 步。
OS 从硬盘把数据读到内核区的 PageCache。
用户进程把数据从内核区 Copy 到用户区。
然后用户进程再把数据写入到 Socket,数据流入内核区的 Socket Buffer 上。
OS 再把数据从 Buffer 中 Copy 到网卡的 Buffer 上,这样完成一次发送。
整个过程共经历两次 Context Switch,四次 System Call。同一份数据在内核 Buffer 与用户 Buffer 之间重复拷贝,效率低下。其中 2、3 两步没有必要,完全可以直接在内核区完成数据拷贝。这也正是Sendfile 所解决的问题,经过 Sendfile 优化后,整个 I/O 过程就变成了下面这个样子。