Kafka(2)从肩周炎到颈椎病——性能

160 阅读8分钟

Kafka性能

Kafka的公司Confluent在2020年8月发布了Benchmarking Kafka vs. Pulsar vs. RabbitMQ: Which is Fastest? (confluent.io),这个文档对比了Kafka、Pulsar、RabbitMQ的吞吐量、端到端延迟等性能数据。最后得出结论Kafka相对来说性能最好。

throughput-and-latency-quantiles.png

effect.png

Kafka峰值吞吐量,比RabbitMQ 快15倍,比Pulsar快2倍,且仍保持较低延迟。

那么,为什么Kafka可以那么快呢?

  1. 磁盘顺序读写

  2. 利用文件系统和pagecache 机制

  3. 数据零拷贝

  4. 批处理和批量压缩

接下来本文将逐条分析。

基于海量数据的消息队列的性能对比与优化方案 - 中国知网 (cnki.net),该文的实验数据也反映出Kafka在消息队列中具有优势。

一、磁盘顺序读写

comparison.png

数据很老,仅供参考。在某些特定情况下,磁盘顺序访问比随机内存访问更快。一般情况下内存访问还是比磁盘访问快,但磁盘顺序访问也并不慢。

持久队列

顺序读写在 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 缓存进行中转,以此提高效率。

cache.png

Kafka 建立在 JVM 之上,其中:

对象的内存开销非常高,通常是所存储的数据的两倍 (甚至更多)

随着堆中数据的增加,Java的垃圾回收变得越来越复杂和缓慢

 

Kafka 使用文件系统和磁盘高速缓存 pagecache 机制。这种机制的优势是:

  1. 缓存容量大:数据存储紧凑,充分利用所有空闲内存 (非 JVM 内存),且不会会增加 GC 负担

  2. 重启恢复快:如果进程重启,JVM 内的 cache 会失效,但 pagecache 仍然可用

  3. 简化代码:所有保持 cache 和文件系统之间一致性的逻辑现在都被放到了 OS 中

相比于维护尽可能多的 in-memory cache,并且在空间不足的时候将数据 flush 到文件系统,Kafka 把这个过程倒过来。所有数据一开始就被写入到文件系统的持久化日志中,而不用在 cache 空间不足的时候 flush 到磁盘。

若对文件系统和 pagecache 感兴趣,推荐阅读:

简直不要太硬了!一文带你彻底理解文件系统 - 程序员cxuan - 博客园 (cnblogs.com)

Page Cache(页缓存) - wellDoneGaben - 博客园 (cnblogs.com)

三、数据零拷贝

字节拷贝时若格式不同,格式转换会消耗时间,故 Kafka 的 producer,broker 和 consumer 都共享标准二进制消息格式,这样数据块不用修改就能在它们之间传递。

read+write

传统数据传输路径 read+write

tradition.png

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.png

相比传统方式,mmap 减少了一次 CPU 拷贝。mmap 对大文件传输有一定优势,但是小文件可能出现碎片,并且在多个进程同时操作文件时可能产生引发进程崩溃。

2. sendfile

sendfile 是 Linux2.1 后引入的一个系统调用函数,它在内核的文件描述符之间复制数据,避免了数据在内核空间和用户空间之间的拷贝操作。

相比传统方式,sendfile 减少了一次 CPU 拷贝和两次上下文切换。但由于数据不经过用户缓冲区,数据无法被修改。在此基础上还可以进行优化。

sendfile.png  

3. sendfile+SG-DMA

sendfilesg.png

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.png

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,环境优美。报名字不太对味,打骨折。

R-C (1).jpg

 

参考:

Apache Kafka

Kafka 中文文档 - ApacheCN

kafka详解(二)--kafka为什么快 - 子月生 - 博客园 (cnblogs.com)

终于知道Kafka为什么这么快了!_kafka_大头星_InfoQ写作社区

Linux中的零拷贝技术,sendfile,splice和tee之间的区别是什么? - 知乎 (zhihu.com)

【linux】图文并茂|彻底搞懂零拷贝(Zero-Copy)技术 - 知乎 (zhihu.com)

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 5 天,点击查看活动详情