Kafka性能
Kafka的公司Confluent在2020年8月发布了Benchmarking Kafka vs. Pulsar vs. RabbitMQ: Which is Fastest? (confluent.io),这个文档对比了Kafka、Pulsar、RabbitMQ的吞吐量、端到端延迟等性能数据。最后得出结论Kafka相对来说性能最好。
Kafka峰值吞吐量,比RabbitMQ 快15倍,比Pulsar快2倍,且仍保持较低延迟。
那么,为什么Kafka可以那么快呢?
-
磁盘顺序读写
-
利用文件系统和pagecache 机制
-
数据零拷贝
-
批处理和批量压缩
接下来本文将逐条分析。
基于海量数据的消息队列的性能对比与优化方案 - 中国知网 (cnki.net),该文的实验数据也反映出Kafka在消息队列中具有优势。
一、磁盘顺序读写
数据很老,仅供参考。在某些特定情况下,磁盘顺序访问比随机内存访问更快。一般情况下内存访问还是比磁盘访问快,但磁盘顺序访问也并不慢。
持久队列
顺序读写在 Kafka 中用 persistent queue 实现,持久队列将正在进行的消息队列数据存储到磁盘,以防在异常终止期间丢失数据。它建立在简单的读取和向文件后追加两种操作之上。这种架构的优点在于所有操作复杂度都是O(1),而且读操作不会阻塞写操作,读操作之间也不会互相影响。
read-ahead 和 write-behind
现代操作系统还提供了 read-ahead 和 write-behind 技术对磁盘读写进行优化,read-ahead 是以大的data block 为单位预先读取数据填充到缓存 cache,而 write-behind 是将多个小型的逻辑写在 cache 中合并,并一次性写入物理磁盘。read-ahead 和 write-behind 只适用于顺序读写。
二、利用文件系统和 pagecache 机制
说到 cache,直接在磁盘上操作,速度较慢。所以内存会申请一个缓存,让操作通过 cache 缓存进行中转,以此提高效率。
Kafka 建立在 JVM 之上,其中:
对象的内存开销非常高,通常是所存储的数据的两倍 (甚至更多)
随着堆中数据的增加,Java的垃圾回收变得越来越复杂和缓慢
Kafka 使用文件系统和磁盘高速缓存 pagecache 机制。这种机制的优势是:
-
缓存容量大:数据存储紧凑,充分利用所有空闲内存 (非 JVM 内存),且不会会增加 GC 负担
-
重启恢复快:如果进程重启,JVM 内的 cache 会失效,但 pagecache 仍然可用
-
简化代码:所有保持 cache 和文件系统之间一致性的逻辑现在都被放到了 OS 中
相比于维护尽可能多的 in-memory cache,并且在空间不足的时候将数据 flush 到文件系统,Kafka 把这个过程倒过来。所有数据一开始就被写入到文件系统的持久化日志中,而不用在 cache 空间不足的时候 flush 到磁盘。
若对文件系统和 pagecache 感兴趣,推荐阅读:
三、数据零拷贝
字节拷贝时若格式不同,格式转换会消耗时间,故 Kafka 的 producer,broker 和 consumer 都共享标准二进制消息格式,这样数据块不用修改就能在它们之间传递。
read+write
传统数据传输路径 read+write
DMA (Direct Memory Access):直接内存访问。DMA 是一种无需 CPU 参与,让外设和系统内存之间进行双向数据传输的硬件机制。使用 DMA 可以释放系统 CPU 的 I/O 数据传输,提高系统的吞吐率。
这显然是低效的,有四次 copy 操作、四次上下文切换和两次系统调用 read() 和 write()。为减少用户态和内核态的转换,提高效率,可以使用零拷贝技术。
零拷贝并不是没有拷贝数据,而是减少用户态和内核态之间的切换次数以及 CPU 拷贝的次数。目前零拷贝技术的几个实现手段包括:mmap+write、sendfile、sendfile+SG-DMA、splice等。
1. mmap+write
Memory Mapped Files:简称 mmap,是 Linux 提供的一种内存映射文件的机制。mmap 会直接把内核缓冲区里的数据映射到用户空间,这样操作系统内核与用户空间就不需要再进行任何的数据拷贝操作。完成映射之后对物理内存的操作会被同步到硬盘上。
相比传统方式,mmap 减少了一次 CPU 拷贝。mmap 对大文件传输有一定优势,但是小文件可能出现碎片,并且在多个进程同时操作文件时可能产生引发进程崩溃。
2. sendfile
sendfile 是 Linux2.1 后引入的一个系统调用函数,它在内核的文件描述符之间复制数据,避免了数据在内核空间和用户空间之间的拷贝操作。
相比传统方式,sendfile 减少了一次 CPU 拷贝和两次上下文切换。但由于数据不经过用户缓冲区,数据无法被修改。在此基础上还可以进行优化。
3. sendfile+SG-DMA
sendfile+SG-DMA 只进行了两次 DMA 拷贝,两次上下文切换。这就是零拷贝 (zero-copy) 技术,没有通过CPU 来拷贝数据,所有的数据都是通过 DMA 来进行传输的。
但是其仍无法对数据进行修改,并且需要硬件层面DMA的支持,sendfile只能将文件数据拷贝到socket描述符上,有一定的局限性。
文件描述符
简称 fd,当应用程序请求内核打开/新建一个文件时,内核会返回一个文件描述符用于对应这个打开/新建的文件,其本质上就是一个非负整数,读写文件也需要使用文件描述符来指定待读写的文件。sendfile(2) - Linux manual page (man7.org)
Scatter-gather DMA
简称 SG-DMA,它使用一个链表描述物理上不连续的存储空间,然后把链表首地址告诉 DMA master。DMA master 在传输完一块物理连续的数据后,不用发起中断,而是根据链表来传输下一块物理上连续的数据,直到传输完毕后再发起一次中断。DMA技术和及其SG模式_chinamaoge的博客-CSDN博客_sg模式dma
4.splice方式
splice 系统调用是 Linux 在 2.6 版本引入的,其不需要硬件支持,并且不再限定于 socket 上,可实现两个普通文件之间的数据零拷贝。
splice() 在两个文件描述符之间移动数据,而无需在内核地址空间和用户地址空间之间进行复制。其中一个文件描述符必须是管道,sendfile(2) - Linux manual page (man7.org)
四、批处理和批量压缩
小型的I/O操作发生在客户端和服务端之间以及服务端自身的持久化操作中。
大量的小型I/O操作会使系统性能低下。为了避免这种情况,Kafka协议建立在一个“message set消息集”的抽象基础上,将消息合理的分组。这使得网络请求将多个消息打包成一组,而不是每次发送一条消息,从而分担网络中往返的开销。Consumer每次获取多个大型有序消息集,并由服务端依次将消息集一次加载到它的日志中。
批处理
批处理可以配置消息数量,也可以指定延迟时间 (如64k /10ms),这允许汇总更多的数据后再发送,在服务器端也会减少更多的IO操作。并给出了一个机制,通过权衡少量额外的延迟时间获取更好的吞吐量。
这个简单的优化对速度有着数量级的提升。批处理操作允许更大的网络数据包,更大的顺序读写磁盘操作,获得连续的内存块等。
端到端的批量压缩
Kafka 支持一批消息可以压缩在一起发送到服务器。这批消息将以压缩格式写入,并且在日志中保持压缩,只会在 consumer 消费时解压缩。
Kafka 支持 GZIP,Snappy 和LZ4 压缩协议。
最后
请你们吃磁盘顺序肚、文件系统和 pagecache 鸡、数据零拷贝、批处李和批量鸭。店名皮卡丘,地址 39°N,100°E,环境优美。报名字不太对味,打骨折。
参考:
kafka详解(二)--kafka为什么快 - 子月生 - 博客园 (cnblogs.com)
终于知道Kafka为什么这么快了!_kafka_大头星_InfoQ写作社区
Linux中的零拷贝技术,sendfile,splice和tee之间的区别是什么? - 知乎 (zhihu.com)
【linux】图文并茂|彻底搞懂零拷贝(Zero-Copy)技术 - 知乎 (zhihu.com)
开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 5 天,点击查看活动详情