一、磁盘顺序写保证写高性能

kafka 对硬盘的写入的模式是顺序写,而不是随机 I/O。在顺序写的情况下,速度可与内存持平。
同时利用了操作系统的页缓存(Page Cache),数据写入缓存后,立即返回,不需等待数据被持久化到磁盘。然后由操作系统决定何时把 Page Cache 的数据真正写入到磁盘中。
同时利用了 mmap,mmap其实就是把物理上的磁盘文件的一些地址和page cache地址进行一层映射,使得进程像读写硬盘一样读写内存,定时有os帮助我们将数据刷写到磁盘。

二、零拷贝机制保证读高性能

在非零拷贝模式下,应用程序想把磁盘数据通过网卡发出去需要以下步骤:

  1. 操作系统将数据从磁盘拷贝到内核区的 page cache;
  2. 用户程序把数据从 page cache 拷贝到用户区缓存;
  3. 用户程序将数据从用户区缓存拷贝到 socket 缓存;
  4. 操作系统将 socket 缓存的数据拷贝到网卡的 buffer 上发送数据。

如下图所示:

kafka recordaccumulator缓存大小设置的太小会导致什么 kafka做缓存_数据


进行了2次上下文切换和4次系统调用。其实可以省去第 2和 3 步骤,由内核直接读取数据发送到 网卡。

三、日志分段保存

kakfa 在磁盘的文件目录布局是:
主题的日志文件按分区划分为多个文件夹,文件夹命名为-
以(topic-分区号)作为目录,目录下有 .log 和 .index 和 .timeindex 文件,如下所示:

-rw-r--r--  1 zhangsan  admin     72 Oct 24 22:15 00000000000000000014.index
-rw-r--r--  1 zhangsan  admin  41805 Oct 24 22:15 00000000000000000014.log
-rw-r--r--  1 zhangsan  admin      0 Oct 24 22:15 00000000000000000014.timeindex

-rw-r--r--  1 zhangsan  admin     80 Oct 24 22:15 00000000000000000536.index
-rw-r--r--  1 zhangsan  admin  41760 Oct 24 22:15 00000000000000000536.log
-rw-r--r--  1 zhangsan  admin      0 Oct 24 22:15 00000000000000000536.timeindex

文件夹内的日志文件分为多个 segment,每个 segment 包含三个文件:

  • xxx.log: 消息内容主文件,记录消息内容信息
  • xxx.index: 偏移量索引文件,记录偏移量到消息位置的映射
  • xxx.timeindex: 时间戳索引文件,记录时间戳到偏移量的映射

首先关注一下每个 segment 的命名,文件名称为20位整数,不够用0填充,数字表示当前 log 文件中第一条消息的偏移量。
.indedx 文件产生的规则是,每当 .log 文件中写入了 4kb 的消息,则会在 .index 文件中新增一条记录,.index 里面是一种稀疏的索引。
比如:

offset: 588 position: 4160
offset: 640 position: 8320
offset: 692 position: 12480
offset: 744 position: 16640
offset: 796 position: 20800

假设现在有消费者,需要从 offset 为700的位置消费数据。假设没有索引文件的情况下,需要从 .log 文件的第一行开始依次往下一次遍历查找,性能比较低。
kafka 的做法是:

  1. 先从跳表中查询出这条日志在哪个日志分段中(Kafka 会在跳表中缓存每个日志分段的起始 offset);
  2. 确定偏移量位置,通过二分法确定相对偏移为 700 这条数据在 692-744 之间,然后根据 692 这条数据的物理位置 12480,直接定位到 log 文件的这条记录;
  3. 依次往下查找,直到找到 offset 为700的这条数据。