Kafka 号称百万 TPS,但换个系统就达不到——因为它的高性能依赖 Linux 内核的特定设计。Kafka 是最懂内核的中间件。
利用 Page Cache 替代自己的缓存、利用顺序写获取极致吞吐、利用零拷贝绕过用户态——这些全部依赖 Linux 内核特性。
01
PART 01:Page Cache 替代 Kafka 缓存
Kafka 没有自己的缓存层,完全依赖操作系统的 Page Cache。JVM 堆建议 < 总内存 50%,剩余留给 Page Cache。
16GB 机器: JVM 堆 8GB, Page Cache 8GB+
为什么 Kafka 不自己缓存?
1. 无 GC 压力:Java GC 对大堆不友好
2. 无双缓冲浪费:应用层 + Page Cache 各存一份是浪费
3. 所有消费者共享 Page Cache:高效的数据复用
# 调高脏页阈值(Kafka 场景)vm.dirty_ratio = 40vm.dirty_background_ratio = 20# 让 Page Cache 容纳更多数据再刷盘,提升吞吐
Kafka 每个 Partition 在磁盘上是顺序文件,生产者追加写入文件末尾,消费者顺序读取。
顺序写 vs 随机写的性能差异:
4KB 随机写: ~30 MB/s 10000 IOPS1MB 顺序写: ~500 MB/s 500 IOPS吞吐量差距: 16 倍
这就是 Kafka 比传统消息队列快 10 倍的根本原因。传统 MQ 消费后删除消息产生随机写,Kafka 只追加不删除。
log.segment.bytes = 1073741824 # 1GB 滚动一个新段log.retention.hours = 168 # 保留 7 天log.flush.interval.ms = 1000 # 刷盘间隔log.flush.interval.messages = 10000 # 消息数触发刷盘
Kafka 消费者读取数据时使用 FileChannel.transferTo(),底层调用 sendfile 系统调用。
传统 IO 路径(4 次拷贝):磁盘 -> Page Cache -> 用户缓冲区 -> Socket 缓冲区 -> 网卡 DMA CPU CPU DMAsendfile 零拷贝(2 次拷贝):磁盘 -> Page Cache -> 直接到网卡 DMA DMA
1GB 数据传输对比:
- 传统路径:CPU 占用 ~30%,传输时间 ~2s
- sendfile:CPU 占用 ~5%,传输时间 ~1.5s
消费者越多,零拷贝的优势越明显。100 个消费者同时读取同一份数据,传统路径 CPU 占用爆炸。
Kafka 使用 Java NIO 封装 epoll。
Acceptor(接收连接) +-- Processor 1 (epoll 事件循环, CPU 0) +-- Processor 2 (epoll 事件循环, CPU 1) +-- Processor N (epoll 事件循环, CPU N)num.network.threads = 8 # = CPU 核数num.io.threads = 8 # 处理实际读写
系统参数:
net.core.somaxconn = 65535net.ipv4.ip_local_port_range = 1024 65535fs.file-max = 2000000
JVM 堆太大(> 16GB)时 GC pause 可能超过 1 秒。推荐 G1GC 回收器。
KAFKA_HEAP_OPTS="-Xmx8G -Xms8G"KAFKA_JVM_PERFORMANCE_OPTS="-XX:+UseG1GC -XX:MaxGCPauseMillis=20"
核心原则:JVM 堆只用于元数据和少量缓存,数据缓存全部交给 Page Cache。堆大小建议 4-8GB。
# 1. Page Cache 充足free -h # available > 总内存 30%# 2. 无 swapfree -h | grep Swap # used = 0# 3. 磁盘顺序写性能dd if=/dev/zero of=/tmp/test bs=1M count=4096 oflag=direct# 结果 > 500 MB/s# 4. GC 频率grep "GC" /var/log/kafka/kafkaServer.log | tail# 5. 网络延迟ping -c 10 broker-host # RTT < 1ms
Kafka 的高性能 = Page Cache(60%+ 内存) + 磁盘顺序写(100x 优势) + sendfile 零拷贝(CPU 降 5 倍) + epoll 事件模型(万级连接)。全部依赖 Linux 内核特性。
END