Kafka-之資料日誌儲存(格式變化與壓縮)

穩哥的哥發表於2020-12-28

Kafka-之資料日誌儲存

1 kafka的日誌佈局

kafka的資料儲存是基於檔案系統的,kafka的資料以日誌的形式儲存在磁碟上,具體的日誌佈局可以看下圖。

在這裡插入圖片描述

很明顯,kafka以topic來進行資料劃分,我們可以通過在server.properties檔案指定log.dirs來指定資料日誌儲存

#指定日誌儲存路徑
log.dirs=/tmp/kafka-logs1,tmp/kafka-logs2,....

雖說kafka按照topic進行資料劃分,但是在物理儲存上還是按照topic-partition進行目錄劃分,假如這裡有個topicA有2個partition,那麼它的儲存結構如下:

cd /tmp/kafka-logs1/
		topicA-0/
				#在每個分割槽下的資料又是按日誌分段進行儲存的,每個日誌分段LogSegment都有自己的索引檔案和輔助檔案
        00000000000000000000.index # offset索引檔案
        00000000000000000000.log	 # 實際資料日誌	
        00000000000000000000.timeindex  # 時間戳索引檔案			
        00000000000000000000.txnindex   # unknown
        00000000000000000000.snapshot   # 快照記錄檔案

    topicA-1/
    		.............

kafka的訊息都是順序寫入磁碟的logSegment檔案的,只有最後一個logSegment檔案才能執行寫入操作,在其之前的所有xxx.log都不能寫入,通常我們將最後一個LogSegment稱為activeSegment,之後追加的訊息將寫入activeSegment,當其大小達到一定程度時就會重新生成一個新的activeSegment的.log檔案。

為了方便日誌分段檔案檢索,每個LogSegment都有對應的標識(baseoffset)和索引檔案。

  • .index(offset索引檔案,用來檢索offset)
  • .timeindex(時間戳索引,用來檢索時間戳)
  • baseoffset(該值代表每個segment檔案的資料的起始offset,如00000000000000133.log,那麼該分段的起始offset是133,如果上一個分段檔案為00000000000000000.log,那麼上個分段檔案中儲存了133條訊息)

還可能出現.delete,.cleaned,.swap,.txnindex,leader_ecpoch_checkpoint等檔案。

從更加巨集觀的角度來講,kafka的日誌目錄結構如下:

在這裡插入圖片描述

2 kafka日誌格式演變

從kafka-0.8x到目前的2.x版本已經經歷了3個大版本V0,V1,V2

  • V0:0.8x-0.10.0,不包括0.10.0
  • V1:0.10.0到0.11.0版本之前,不包括0.11.0
  • V2:0.11.0到如今

2.1 V0的訊息格式

在這裡插入圖片描述

上圖左邊的就是V0版本的Record格式,通常通過LOG_OVERHEADRECORD_OVERHEAD同時來描述一個訊息,右邊的Message Set是訊息容器,用於儲存訊息。

  • crc32: crc32校驗值,作用範圍magic~value之間 (4B
  • magic: 訊息版本號,這裡是0 (1B
  • attributes:訊息屬性,0-3代表壓縮格式(1B
    • 0代表NONE
    • 1代表GZIP
    • 2代表SNAPPY
    • 3代表LZO4(0.9x引入)
  • key length代表key的長度(4B),如果為-1,代表key為null,是沒有設定key的
  • key代表訊息的key
  • value length代表value的長度(4B
  • value代表訊息

在V0版本中訊息的最小長度為 4 + 1 + 1 + 4 + 4 = 14B(byte),如果小於這個值那麼,那麼這個訊息就是一條破損的訊息而不被接收,我們可以使用kafka內部的工具類檢視置頂的segment檔案的RECORD_OVERHEAD_V0(訊息的最小值)資訊

./kafka-run-class.sh kafka.tools.DumpLogSegments --files /tmp/kafka-logs/topic1-0/00000000000033568928.log 

>>>>>>
#這裡將會顯示starting offset、offset、position、isvalid、payloadsize、magic、compressCodec、crc、keysize資訊,當前演示版本為kafka-0.8.2.1
Starting offset : 33568928
offset: 33568928 position : 0 isvalid: true payloadsize: 5 magic: 0 
compresscodec: NoCompressionCodec crc: 592888119 keysize : 3

2.2 V1的訊息格式

在這裡插入圖片描述

除了新引入了一個timestamp,magic=1,其它的都是與V0相同,timestamp的大小為8B,所以V1版本的最小訊息(RECORD_OVERHEAD_V1)為14+8=22B(byte)。

  • 該timestamp欄位由broker端的引數log.message.timestamp.type指定,預設是生產者建立訊息的時間createTime;
  • 我們可以通過在建立ProducerRecord的時候進行指定,new ProducerRecord(,timestamp);
  • 如果沒有顯式手動指定timestamp,那麼就會選擇time.milliseconds();
  • V1與V0一樣,都是通過LOG_OVERHEADRECORD_OVERHEAD來描述一個message。

2.3 V2的訊息格式

V2版本的訊息集從Message Set變成了Record Batch,這是一個處理批量訊息讀寫的NIO 通道channel,在訊息壓縮的過中,Record Batch Header部分是不會被壓縮的(也就是下圖中first offset到records count部分),被壓縮的是records欄位中的所有內容,每個Records中包含一條或多條Record。

  • Record Batch對應Producer client中的Producerbatch
  • Record對應Producer Record中的ProducerRecord

在這裡插入圖片描述
V2版本的Record格式去除了crc,將其移動到了RECORDBATCH,另外增加了length、timestamp delta 、offset delta;key、key length、value、value length還是與V0,V1保持一致,具體的新增的欄位如下。

  • length:訊息的總長度;
  • attributes:欄位在V2中雖然沒被使用,但是任然為其保留1B的空間,保證可擴充套件;
  • timestamp delta:時間戳增量,是當前訊息的時間戳與RecordBatch的起始時間戳的差值,通常儲存一個時間戳需要8個位元組,這裡儲存的差值進一步減少了儲存的消耗;
  • offset delta:位移增量,如果資料條目特別多,那麼儲存offset也是需要較多的儲存,這裡儲存的是當前訊息的offset與RecordBatch起始offset的差值,進一步節省儲存空間;
  • headers:用來儲存應用級別的擴充套件。

除了對RECORD本身做了修改,在V2版本還對RECORDBATCH進行了徹底的修改,除了對上面的crc校驗,同時還新增瞭如下欄位。

  • first offset:這個RECORBATCH的起始位移
  • length:從partition leader epoch欄位到末尾的總長度,並不是訊息的總長度
  • partition leader epoch:可以看作leader的版本號,或者看作分割槽leader的更新次數
  • magic: 訊息格式的版本號
  • attributes:訊息屬性,0~3位依舊是壓縮型別
  • last offset delta :RECORDBATCH中的最後一個RECORD的offset與first offset的差值(增量)
  • first timestamp:第一條訊息的時間戳
  • producer id:生產者id:PID,用來與first sequence一起保證單個生產者-分割槽-單個session的冪等性
  • producer epoch:與producer id一樣,用來支援事物與冪等(遞增序列)
  • first sequence:與producer id,producer epoch一樣,用來保證事物與冪等(遞增序列)
  • records count:該RECORDBATCH中RECORD的條目數

2.4 訊息壓縮

訊息越多壓縮的效果越明顯,資料比較少的時候選擇壓縮反而不太好,通常kafka的訊息在生產者這裡就開始選擇壓縮,在Broker上也是以壓縮的形式進行儲存的,只有在消費資料處理資料的術後才會將訊息進行解壓,這樣就保證了端到端的壓縮。在kafka的引數中,壓縮格式通過compress.type來指定,預設為producer,表示保留生產者使用的壓縮方式,還可以選擇lz4,snappy,gzip等配置,分別表示使用LZ4,SNAPPY,GZIP的壓縮演算法

注:壓縮率 = 壓縮後大小 /壓縮前大小 * 100%,壓縮率通常是越小越好,而口語中常說壓縮率越高越好。

kafka中有 2個概念,不能混淆

  • compress message 使用壓縮演算法將訊息進行壓縮,減少記憶體,網路io,與磁碟的消耗
  • compact message 將日誌中的訊息進行清理,類似於hbase的compact操作

在這裡插入圖片描述
訊息的壓縮是針對整個訊息集RECORDBATCH(MESSAGE SET),將整個訊息集壓縮成wrapper message 和inner message,如下圖

該途中offset為RECORDBATCH的first offset,內層訊息中的offset為每個 record的offset與外層offset的增量,即(x - first offset)。

相關文章