为什么Kafka这么快?

很多人都知道Kafka吞吐量高,延迟低,那么为什么Kafka会这么快?如果在面试中被问到这个问题,你会回答吗?如果你一脸懵逼地看着面试官,面试官可能不会很生气,但是后果可能很严重。因为在面试官的眼里,你可能只是个会写几行消费者代码的Kafka初级用户。

别急,看完这篇短文再去面试,一定会让面试官对你刮目相看。

一、顺序IO

首先,Kafka使用了顺序IO(Sequential IO),并极力避免随机磁盘访问(Random Disk Access)。前者的写入速度比后者快了一个数量级,比如在一个由6块7200转SATA硬盘组成的磁盘阵列上,顺序写入的速度可以达到300MB/S,而随机写入速度只有50KB/S。差距如此之大,难怪Kafka会快得飞起来。

Kafka所采用的提交日志就是以追加的方式写入分区的,就是说单个分区的写入是可以保证顺序的,没有删除和更新操作,因此避免了随机写入。另外,从分区读取数据的时候也是按顺序读取的,避免了随机读取。

图片

那么问题来了,就算顺序IO再快,也快不过内存,那么为什么Kafka不用内存来保存数据呢?第一个原因大概所有人都知道,内存虽快,但比硬盘要贵得多。Kafka作为一个大数据生态系统的一员,是为保存海量数据而生的,使用内存来保存海量数据显然是不现实的。另外,Kafka的高可用是通过创建多个副本来实现的,一个消息可能会被复制三份五份,这无疑又增加了存储开销,使用内存来存储就更是天方夜谭。除此之外,Kafka运行在JVM上,如果内存堆中的对象太多,必然会在垃圾回收时造成严重的延迟,从而影响系统的整体性能。

二、内存映射文件

内存映射文件将磁盘上的文件内容与内存映射起来,我们往内存里写入数据,操作系统会在稍后把数据冲刷到磁盘上。所以,在写入数据时几乎就是写入内存的速度,这是Kafka快到飞起的另一个原因。

那么问题又来了,既然可以使用内存映射文件,那么为什么不直接使用内存呢?这个问题已经回答过了,就不再累述了。

三、零拷贝

当Kafka客户端从服务器读取数据时,如果不使用零拷贝技术,那么大致需要经历这样的一个过程:

  1. 操作系统将数据从磁盘上读入到内核空间的读缓冲区中。
  2. 应用程序(也就是Kafka)从内核空间的读缓冲区将数据拷贝到用户空间的缓冲区中。
  3. 应用程序将数据从用户空间的缓冲区再写回到内核空间的socket缓冲区中。
  4. 操作系统将socket缓冲区中的数据拷贝到NIC缓冲区中,然后通过网络发送给客户端。

图片

从图中可以看到,数据在内核空间和用户空间之间穿梭了两次,那么能否避免这个多余的过程呢?当然可以,Kafka使用了零拷贝技术,也就是直接将数据从内核空间的读缓冲区直接拷贝到内核空间的socket缓冲区,然后再写入到NIC缓冲区,避免了在内核空间和用户空间之间穿梭。

图片

可见,这里的零拷贝并非指一次拷贝都没有,而是避免了在内核空间和用户空间之间的拷贝。如果真是一次拷贝都没有,那么数据发给客户端就没了不是?不过,光是省下了这一步就可以带来性能上的极大提升。

四、应用层面的优化

除了利用底层的技术外,Kafka还在应用程序层面提供了一些手段来提升性能。最明显的就是使用批次。在向Kafka写入数据时,可以启用批次写入,这样可以避免在网络上频繁传输单个消息带来的延迟和带宽开销。假设网络带宽为10MB/S,一次性传输10MB的消息比传输1KB的消息10000万次显然要快得多。

写在后面

需要注意的是,作为大数据生态系统的一员,Kafka在设计之初就是为了满足高吞吐量、低延迟、高可用等方面的需求,所以这些技术可以派上用场。但如果换成其他消息中间件,就不一定也能照搬这些技术,具体还是要根据产品的特点来决定。