Kafka的高性能磁盤讀寫實現原理

一、概述

  • Kafka作為一個支持大數據量寫入寫出的消息隊列,由於是基於Scala和Java實現的,而Scala和Java均需要在JVM上運行,所以如果是基於內存的方式,即JVM的堆來進行數據存儲則需要開闢很大的堆來支持數據讀寫,從而會導致GC頻繁影響性能。考慮到這些因素,kafka是使用磁盤而不是kafka服務器broker進程內存來進行數據存儲,並且基於磁盤順序讀寫和MMAP技術來實現高性能。

二、存儲結構

目錄與文件結構

  • 由之前的文章分析可知,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
  • 由於在本機存在3個brokers,對應的server.properties的broker.id分別為:0, 1, 2,所以通過–describe選項查看的mytopic的詳細信息可知:
  • mytopic的分區0是分區leader為broker2,同步副本為broker1和broker2;分區1的分區leader為broker0,同步副本為broker0和broker2。
  • 以上命令對應的主題mytopic存在兩個分區和每個分區存在兩個分區副本,其中broker0,broker1和broker2對應的數據目錄在server.properties文件配置的log.dirs分別為;
  • /tmp/kafka-logs,/tmp/kafka-logs2,/tmp/kafka-logs3。所以mytopic主題對應的兩個分區在磁盤上的目錄結構如下:
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的另外一個分區。

文件內容

  • kafka的數據文件是二進制格式的文件,因為二進制的文件大小相對於文本文件更小,所以可以減少數據傳輸,複製量,提高數據傳輸速度,節省網絡帶寬。
  • 在分區目錄下面除了存在數據文件之外,還存在一個索引文件,索引文件的作用是加快在數據文件的檢索速度,索引文件也是二進制文件,如下以index結尾的就是索引文件,以log結尾的就是數據文件:
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
  • 整個分區的數據不是由一個數據文件存放的,而是由多個segments組成的,即上面看到的0000.log文件是其中一個segment文件,文件名是以該文件的第一個數據相對於該分區的全局offset命名的。每當segment文件達到一定的大小,則會創建一個新的segment文件,具體大小在server.properties配置:默認為1G。
# The maximum size of a log segment file. When this size is reached a new log segment will be created.
log.segment.bytes=1073741824
  • 而索引文件的文件內容也是offset的稀疏索引,從而在消費者消費消息時,broker根據消費者給定的offset,基於二分查找先在索引文件找到該offset對應的數據segment文件的位置,然後基於該位置(或往下)找到對應的數據。
  • 具體內容格式如圖所示:
Kafka的高性能磁盤讀寫實現原理


三、消息寫入

磁盤順序寫

  • 當broker接收到producer發送過來的消息時,需要根據消息的主題和分區信息,將該消息寫入到該分區當前最後的segment文件中,文件的寫入方式是追加寫。
  • 由於是對segment文件追加寫,故實現了對磁盤文件的順序寫,避免磁盤隨機寫時的磁盤尋道的開銷,同時由於是追加寫,故寫入速度與磁盤文件大小無關,具體如圖:
Kafka的高性能磁盤讀寫實現原理

頁緩存PageCache

  • 雖然消息寫入是磁盤順序寫入,沒有磁盤尋道的開銷,但是如果針對每條消息都執行一次磁盤寫入,則也會造成大量的磁盤IO,影響性能。
  • 所以在消息寫入方面,broker基於MMAP技術,即內存映射文件,將消息先寫入到操作系統的頁緩存中,由頁緩存直接映射到磁盤文件,不需要在用戶空間和內核空間直接拷貝消息,所以也可以認為消息傳輸是發送在內存中的。
  • 由於是先將消息寫入到操作系統的頁緩存,而頁緩存數據刷新同步sync到磁盤文件是由操作系統來控制的,即操作系統通過一個內核後臺線程,每5秒檢查一次是否需要將頁緩存數據同步到磁盤文件,如果超過指定時間或者超過指定大小則將頁緩存數據同步到磁盤。所以如果在刷新到磁盤文件之前broker機器宕機了,則會導致頁緩存的數據丟失。
  • 使用頁緩存的另外一個好處是,如果重啟了kafka服務端(這個服務重啟,而不是機器重啟),頁緩存中的數據還是可以繼續使用的。

四、消息讀取

  • 消費者負責向broker發送從某個分區讀取消費消息的請求,broker接收到消費者數據讀取請求之後,根據消費者提供主題,分區與分區offset信息,找到給定的分區index和segment文件,然後通過二分查找定位到給定的數據記錄,最後通過socket傳輸給消費者。

零拷貝讀取

  • broker在從segment文件讀取消息然後通過socket傳輸給消費者時,也是基於MMAP技術實現了零拷貝讀取。

傳統IO與socket傳輸

  • 對於傳統的socket文件讀取傳輸的過程為:
  1. 操作系統將磁盤文件數據讀取到內核空間的頁緩存;
  2. 應用通過系統調用將數據從內核空間讀取到用戶空間的緩存中;
  3. 應用通過系統調用將數據從用戶空間的緩存回寫到內核空間的socket緩衝區;
  4. 操作系統將內核空間的socket緩存區中的數據寫到網卡硬件緩存中,以便將數據發送出去。
  • 所以一次socket文件讀取傳輸涉及到兩次系統調用和四次拷貝。具體如圖所示:
Kafka的高性能磁盤讀寫實現原理

基於MMAP的零拷貝

  • 操作系統提供了sendfile系統調用來支持MMAP機制,即應用只需指定需要傳輸的磁盤文件句柄,然後通過sendfile系統實現磁盤文件讀取和從socket傳輸出去,其中磁盤文件的讀取和從socket傳輸出去都是通過sendfile系統調用在內核完成的,不需要在內核空間和用戶空間進行數據拷貝,具體過程如下:
  1. 應用指定需要傳輸的文件句柄和調用sendfile系統調用;
  2. 操作系統在內核讀取磁盤文件拷貝到頁緩存;
  3. 操作系統在內核將頁緩存內容拷貝到網卡硬件緩存。
  • 故整個過程涉及到一次sendfile系統調用,在內核態完成兩次拷貝,在內核和用戶空間之間不需要進行數據拷貝。具體過程如圖所示:
Kafka的高性能磁盤讀寫實現原理

  • 所以kafka使用sendfile系統,具體為Java的senfile系統調用API: FileChannel的transferTo, transferFrom,基於MMAP機制實現了磁盤文件內容的零拷貝傳輸。同時由於操作系統將磁盤文件內容加載到了內核頁緩存,故消費者針對該磁盤文件的多次請求可以重複使用。


分享到:


相關文章: