Kafka是大型架构的基石,下面我重点详解Kafka如何实现百万吞吐量@mikechen
顺序写入
Kafka 能够实现高吞吐量,其最根本的原因之一:是它将所有数据写入硬盘时,都采用了“顺序写”的方式。
在高并发场景下,Kafka 的写入数据是追加到日志文件末尾,避免随机写入开销。
这样,哪怕数据量非常大,Kafka 也能保持稳定高效的写入性能。
顺序写,能够最大化地利用机械硬盘的性能。
机械硬盘的读写头只需要沿着一个方向移动,几乎不需要寻道。
其顺序写入速度可以达到数百 MB/s,甚至接近 SSD 的性能。
Page Cache
Kafka 并不会每次写入都直接落盘,而是先写入 操作系统内核的 Page Cache。
Page Cache:是操作系统用于缓存磁盘数据的内存区域。
当生产者写入消息到 Kafka,消息被写入到文件系统的 Page Cache,随后由操作系统后台线程(pdflush / writeback)将脏页刷新到磁盘。
消费者读取时,如果数据仍在 Page Cache 中,读取操作不需要磁盘 I/O,从而极大降低延迟并提高吞吐。
批量发送与批量拉取
为了减少网络和磁盘 I/O 的次数,Kafka 在客户端设计上采用了批量处理的策略。
生产者端批量发送,生产者不是每收到一条消息就立即发送。
而是将多条消息缓存起来,攒成一个批次(Batch),然后一次性发送给 Broker。
参数控制: 这由 batch.size
(批次大小)和 linger.ms
(等待时间)两个参数控制。
消费者端批量拉取, 消费者同样采用批量拉取的方式。
它一次性从 Broker 拉取一个或多个批次的消息,而不是一条条拉取。
参数控制: 这由 fetch.min.bytes
和 fetch.max.wait.ms
控制。
零拷贝
在消费者拉取消息时,Kafka 使用了零拷贝技术,避免了不必要的 CPU 和内存开销。
传统数据传输: 在没有零拷贝的情况下,从磁盘文件发送数据到网络通常需要经过四次数据拷贝和两次 CPU 上下文切换。
数据从磁盘读入内核缓冲区,再拷贝到应用缓冲区,然后从应用缓冲区拷贝到 Socket 缓冲区,最后再拷贝到网卡发送。
零拷贝实现: Kafka 使用 sendfile
系统调用(Linux)或等效机制,直接将数据从 Page Cache 发送到网络。数据不需要经过应用缓冲区,因此避免了两次 CPU 拷贝和上下文切换。
这种技术大大减少了数据在内核和用户空间之间的复制开销,从而减轻了 CPU 负担,并显著提高了数据传输的效率和吞吐量。