消息队列性能考虑kafka

Kafka基于磁盘顺序IO和零拷贝技术实现高性能文件读写

2020-12-24  本文已影响0人  tracy_668

[TOC]

一、概述

Kafka作为一个支持大数据量写入写出的消息队列,由于是基于Scala和Java实现的,而Scala和Java均需要在JVM上运行,所以如果是基于内存的方式,即JVM的堆来进行数据存储则需要开辟很大的堆来支持数据读写,从而会导致GC频繁影响性能。考虑到这些因素,kafka是使用磁盘而不是kafka服务器broker进程内存来进行数据存储,并且基于磁盘顺序读写和MMAP技术来实现高性能。

二、存储结构

  1. 目录与文件结构

由之前的文章分析可知,kafka是通过主题和分区来对消息进行分类的,所以在磁盘存储结构方面也是基于分区来组织的,即每个目录存放一个分区的数据,目录名为“主题-分区号”,如mytopic这个主题包含两个分区,则对应的数据目录分别为:mytopic-0和my-topic-1,如下:

./kafka-topics.sh --create --topic mytopic --partitions 2 --zookeeper localhost:2181 --replication-factor 2

xyzdeMacBook-Pro:bin xyz ./kafka-topics.sh --describe --topic mytopic --zookeeper localhost:2181
Topic:mytopic   PartitionCount:2    ReplicationFactor:2 Configs:
    Topic: mytopic  Partition: 0    Leader: 2   Replicas: 2,1   Isr: 2,1
    Topic: mytopic  Partition: 1    Leader: 0   Replicas: 0,2   Isr: 0,2

xyzdeMacBook-Pro:bin xyz cd /tmp/kafka-logs
xyzdeMacBook-Pro:kafka-logs xyz ls
mytopic-1               recovery-point-offset-checkpoint    replication-offset-checkpoint

xyzdeMacBook-Pro:kafka-logs xyz cd /tmp/kafka-logs2/
xyzdeMacBook-Pro:kafka-logs2 xyz ls
mytopic-0               recovery-point-offset-checkpoint    replication-offset-checkpoint

xyzdeMacBook-Pro:kafka-logs2 xyz cd /tmp/kafka-logs3
xyzdeMacBook-Pro:kafka-logs3 xyz ls
mytopic-0               recovery-point-offset-checkpoint
mytopic-1               replication-offset-checkpoint

  1. mytopic-0分区:在broker2对应的数据目录/tmp/kafka-logs3下面存在mytopic-0的主分区,broker1对应的数据目录/tmp/kafka-logs2存放mytopic-0的另外一个分区副本;
  2. mytopic-1分区:在broker0对应的数据目录/tmp/kafka-logs下面存放mytopic-1的主分区,broker2对应的数据目录/tmp/kafka-logs3存放mytopic-1的另外一个分区。

2. 文件内容

xyzdeMacBook-Pro:mytopic-1 xieyizun$ ls -allh
total 0
drwxr-xr-x  4 xieyizun  wheel   128B  4 27 09:56 .
drwxr-xr-x  6 xieyizun  wheel   192B  4 27 20:26 ..
-rw-r--r--  1 xieyizun  wheel    10M  4 27 09:56 00000000000000000000.index
-rw-r--r--  1 xieyizun  wheel     0B  4 27 09:56 00000000000000000000.log
# The maximum size of a log segment file. When this size is reached a new log segment will be created.

log.segment.bytes=1073741824

image.png

三、消息写入:磁盘顺序写

1. 磁盘顺序写

image.png

2. 页缓存PageCache

四、消息读取:MMAP零拷贝

1. 传统IO与socket:两次系统调用,四次内存拷贝

  1. 从磁盘读取数据

(1)操作系统将磁盘文件数据读取到内核空间的页缓存;
(2)应用通过系统调用将数据从内核空间读取到用户空间的缓存中(第一次系统调用);

  1. 将数据写入socket

(3)应用通过系统调用将数据从用户空间的缓存回写到内核空间的socket缓冲区(第二次系统调用);
(4)操作系统将内核空间的socket缓存区中的数据写到网卡硬件缓存中,以便将数据发送出去。

所以一次socket文件读取传输涉及到两次系统调用和四次拷贝。具体如图所示:

image.png
2. 基于MMAP的零拷贝
操作系统提供了sendfile系统调用来支持MMAP机制,即应用只需指定需要传输的磁盘文件句柄,然后通过sendfile系统实现磁盘文件读取和从socket传输出去,其中磁盘文件的读取和从socket传输出去都是通过sendfile系统调用在内核完成的,不需要在内核空间和用户空间进行数据拷贝,具体过程如下:
  1. 应用指定需要传输的文件句柄和调用sendfile系统调用(第一次系统调用);
  2. 操作系统在内核读取磁盘文件拷贝到页缓存(第一次内存拷贝);
  3. 操作系统在内核将页缓存内容拷贝到网卡硬件缓存(第二次内存拷贝)。

故整个过程涉及到一次sendfile系统调用,在内核态完成两次拷贝,在内核和用户空间之间不需要进行数据拷贝。具体过程如图所示:

image.png

所以 kafka 使用 sendfile 系统调用,具体为 Java 的 senfile 系统调用API: FileChannel的transferTo, transferFrom
,基于MMAP机制实现了磁盘文件内容的零拷贝传输(不需要在用户控件和内核空间进行数据拷贝)。

同时由于操作系统将磁盘文件内容加载到了内核页缓存,故消费者针对该磁盘文件的多次请求可以重复使用,避免重复在磁盘和内存之间进行数据拷贝。

上一篇下一篇

猜你喜欢

热点阅读