零拷贝操作就是不对数据进行不必要的拷贝。
在Kafka中是指操作系统将数据从页面缓存直接复制到套接字缓冲区,实际上完全绕过了 Kafka 代理 Java 程序。
在这之前,Kafka 已经在操作系统的页面缓存中 "保留 "了最新写入的数据,以便更快地获取。
这就省去了一些额外的拷贝和用户 <-> 内核模式切换。
如果你的应用程序的工作是从磁盘读取文件并通过网络发送,就会产生大量不必要的副本和用户/内核模式切换。
一些术语:
- - 读缓冲区 - 这是操作系统的页面缓存。
- - 套接字缓冲区 - 这是操作系统管理数据包的字节缓冲区。
- - NIC 缓冲区 - 网卡中的字节缓冲区。
- - DMA 复制 - DMA 是直接内存访问(Direct Memory Access)的缩写,是内存控制器的一项功能,允许硬件(图形卡、声卡、网卡等)访问内存(RAM)而无需 CPU 参与。
假设 我们有 4 个模式开关和 4 个数据拷贝。
- 应用程序启动磁盘 → 操作系统缓冲区 DMA 复制(用户 → 内核模式)
- 读取缓冲区 → 应用程序缓冲区复制(内核 → 用户模式)
- 应用程序 → 套接字缓冲区复制(用户模式 → 内核模式)
- 套接字缓冲区 → 网卡缓冲区 DMA 复制(响应写出后内核 → 用户模式)
在零拷贝中,这种流程是这样:
Kafka 以响应请求的二进制格式存储数据。
执行最初的第 2 和第 3 步毫无意义,因为 Kafka 不会对给定的数据做任何处理,只会将其传回内核。
使用零拷贝后,数据不会拷贝到 Kafka,而是直接进入 NIC 缓冲区。
请注意,这里还有另一项优化--读取缓冲区直接将数据复制到 NIC 缓冲区,而不是套接字缓冲区。
这就是所谓的分散收集操作(又称矢量化 I/O)。
- - 分散收集scatter-gather:仅在套接字缓冲区中存储读缓冲区指针,并让 DMA 引擎直接从内存中读取这些地址。
最终结果如何?
- - 2 个用户/内核模式开关。(少 2 次)
- - 2 份 DMA 副本(相同)
- - 1 份很轻量的指针 CPU 副本。(少 2 个)
总结
Kafka使用零拷贝(Zero-Copy)技术可以有效提升生产力效率。它的原理是:
- 将磁盘文件传送至内核缓冲区,用户通过内存就能修改内核空间。这样可以省去用户到内核空间的数据开销。
- 使用DMA(直接内存访问)技术,网卡可以直接从内核读取数据,而不需要先拷贝到用户空间缓冲区。
- 在Java中,FileChannel的transferTo()方法可以实现零拷贝。它底层基于操作系统的sendfile系统调用,可以将数据从文件描述符直接拷贝到套接字缓冲区,而不经过用户空间。
与传统的数据传输方式相比,Kafka 的零拷贝技术可以减少 CPU 时钟频率、减少内存拷贝次数、提高数据传输效率。
但是,零拷贝并非完全没有数据拷贝,而是相对于用户空间而言,需要通过操作系统来拷贝。这依赖于操作系统和Java NIO的支持来实现。
此外,当启用SSL/TLS时,Kafka就使用零拷贝技术了,因为需要对数据进行解密和在大多数Kafka中,CPU很少成为瓶颈,网络带宽的饱和速度要快得多,所以减少内存对网络性能的影响通常不大。
在大多数 Kafka 部署中,零拷贝的影响并不大,因为CPU 很少是瓶颈。