Skip to content

为什么 Kafka 这么快 ​

Kafka 的高吞吐不是魔法,而是四个机制的叠加:顺序写入、零拷贝、批处理、页缓存。本文逐个拆解。

1. 顺序写入 ​

消息追加到日志文件末尾,永远不修改已有数据。这是 Kafka 高吞吐的根基。

写入方式吞吐量级原因
顺序写可达数百 MB/s 量级磁头顺序移动,无需频繁寻道
随机写仅数 MB/s 量级,相差数个数量级每次写入都要移动磁头,寻道时间以毫秒计

上表对比针对机械硬盘;SSD 没有机械寻道,随机写性能远高于此,顺序写与随机写的差距会明显缩小。

Kafka 把随机写变成了顺序写——消息只能追加到 Partition 的活跃 Segment 末尾,永远不修改、不删除已有数据(删除是整个 Segment 级别的)。

2. 零拷贝 ​

传统方式读取文件发送到网络:

txt
磁盘 → 内核缓冲区 → 用户空间 → Socket 缓冲区 → 网卡
        4 次拷贝,4 次上下文切换

Kafka 使用 sendfile() 系统调用:

txt
磁盘 → 内核缓冲区 → 网卡
        2 次拷贝,2 次上下文切换

数据从不经过用户空间,CPU 开销大幅降低。这在消费者读取消息时生效——Broker 直接把日志文件的内容通过零拷贝发送给消费者。

上面的拷贝次数是明文传输的典型简化。实际路径取决于内核实现、网卡 DMA 等条件;在 Kafka 常见的 TLS 传输实现中,数据需要经过用户空间加解密,无法沿用这条零拷贝路径。

3. 批处理 ​

生产者把多条消息打包成一个请求发送,而不是逐条发送:

java
props.put("batch.size", 65536);    // 64KB
props.put("linger.ms", 20);        // 最多等 20ms
效果说明
减少请求开销批量发送减少请求头、协议处理和请求调度的开销
提高压缩比批次越大,压缩算法越能利用消息间的相似性
减少 Broker 端 I/OBroker 一次写入更大的连续块

4. 页缓存 ​

Kafka 的消息写入操作不直接落到磁盘,而是写入操作系统的页缓存(Page Cache):

txt
Producer → 页缓存 → 异步刷盘
Consumer ← 页缓存(如果消息还没落盘,直接从内存读)
优势说明
写入延迟低写入页缓存是内存操作,通常可减少同步磁盘 I/O 带来的等待
读取命中率高消费者通常读最近写入的消息,这些消息大概率还在页缓存中
不占 JVM 堆页缓存由操作系统管理,不经过 JVM 垃圾回收

需要区分三个概念:写入页缓存只是文件写入通常先经过的操作系统缓存;生产者收到确认由 acks 和副本状态决定,不等于本地磁盘已完成持久化;刷盘由 Broker 配置和操作系统共同决定,不是每条消息写入后立即执行。

Kafka 没有把数据加载到 JVM 堆再返回,而是让操作系统直接管理缓存——这是它比很多消息队列快的关键原因之一。

5. 四个机制的协同 ​

txt
写入路径:
  Producer → 批处理 → 顺序写入页缓存 → 异步刷盘
                ↓
          压缩(批越大压缩比越高)

读取路径:
  Consumer ← 零拷贝 ← 页缓存(或磁盘顺序读)

每个机制都不是 Kafka 发明的,但它们的组合是 Kafka 的核心竞争力。它们也各有边界:顺序写入受存储带宽与复制网络限制;批处理增大批次或等待时间会推高延迟;页缓存在工作集超出可用内存后命中率下降;零拷贝无法突破链路带宽上限,但在带宽饱和时仍能减少 CPU 开销。调优时需结合观测指标判断瓶颈所在,详见吞吐调优。

6. 延伸阅读 ​

Released under the MIT License.