kafka叢集訊息格式之V0版本到V2版本的平滑過渡詳解-kafka 商業環境實戰

凱新雲技術社群發表於2019-03-04

版權宣告:本套技術專欄是作者(秦凱新)平時工作的總結和昇華,通過從真實商業環境抽取案例進行總結和分享,並給出商業應用的調優建議和叢集環境容量規劃等內容,請持續關注本套部落格。版權宣告:禁止轉載,歡迎學習。QQ郵箱地址:1120746959@qq.com,如有任何商業交流,可隨時聯絡。

1 Kafka 訊息格式變遷(滄海桑田)

從0.8.x版本開始到現在的1.1.x版本,Kafka的訊息格式也經歷了3個版本。每次版本的改變,都預示著新的優化。那麼Broker作為Kafka服務載體,承擔了訊息協議的響應和接收。

  • 持久化訊息。
  • 把訊息從傳送端過渡到消費端。

2 JVM訊息重排機制(Java物件的繁重之軀)

  • Java記憶體模型儲存物件的開銷很大,甚至可能需要花費比訊息大兩倍的空間來儲存資料。為了降低這種開銷,JMM(Java Memory Model)會對使用者自定義的類進行欄位重排。
  • 垃圾回收隨著堆上資料的擴張,會從整體上拖累應用程式的吞吐量。
  • JMM要求物件必須按照8位元組對齊,未對齊的部分會填充空白字元進行補齊padding。
kafka叢集訊息格式之V0版本到V2版本的平滑過渡詳解-kafka 商業環境實戰
  • 對齊填充計算方法:
    HotSpot 的對齊方式為 8 位元組對齊,不足的需要 Padding 填充對齊, 公式:

          (物件頭 + 例項資料 + padding)% 8 == 0 (0<= padding <8)
    複製程式碼
  • 對一個java物件,至少需要16位元組物件頭部(對於64位JVM物件通常由8位元組的Word組成)。

3 Kafka 輕裝上陣物件儲存

  • kafka採用Java NIO的ByteBuffer來儲存訊息,同時依賴檔案系統提供的頁快取機制,不再依賴java的堆快取。悖論:寫檔案系統時,如果java的堆快取儲存一份物件,那麼頁快取還會儲存一份,何必呢?
  • ByteBuffer是緊湊的二進位制位元組結構,不需要padding,因此可以省去很多不必要的記憶體開銷。
  • 在一個64G記憶體的機器上,kafka可以使用記憶體到58-62GB之間,不用擔心Java GC 。
  • ByteBuffer可以節省大量空間,相比於java的堆快取方案。

4 V0(訊息元祖)=> 14位元組+12(LOG_OVERHEAD)

kafka叢集訊息格式之V0版本到V2版本的平滑過渡詳解-kafka 商業環境實戰
  • 版本號: V0版本magic=0,V1版本magic=1,V2版本magic=2

  • 屬性:訊息壓縮型別。目前僅支援3種壓縮方法。

              0X00 未啟動壓縮 0x01 GZIP  0x02 Snappy  0x03 LZ4
    複製程式碼
  • 注意key長度欄位和value長度欄位是固定的,沒有也佔用4個位元組,來存 -1

  • 除了key值和value值外,可以統稱為是訊息頭部資訊(header),總共佔用14位元組。

      假設:存在Key值 為key , value值為 value  (一個字元一個位元組,共8個位元組)
      
      則 header 14 位元組 + 值 8位元組 = 22位元組
      
      當key為空時,則佔用 19位元組
    複製程式碼
  • 日誌頭部(LOG_OVERHEAD):每個Record(v0和v1版)必定對應一個offset和message size。每條訊息都一個offset用來標誌它在partition中的偏移量,這個offset是邏輯值,而非實際物理偏移值,message size表示訊息的大小,這兩者的一起被稱之為日誌頭部(LOG_OVERHEAD),固定為12B

4.1 V0集合(被V2Batch取代)

kafka叢集訊息格式之V0版本到V2版本的平滑過渡詳解-kafka 商業環境實戰

總結:一條訊息必定包含LOG_OVERHEAD和訊息體兩部分。最小佔用12B+14B=26B,在不包含key值和Value值的情況下。

若key =key , value=value 則佔用26(純格式)+8(值空間)=34B

   首先建立一個partition數和副本數都為1的topic,名稱為“msg_format_v0”,
   然後往msg_format_v0中傳送一條key=”key”,value=”value”的訊息,之後檢視對應的日誌:

    -rw-r--r-- 1 root root       34 Apr 26 02:52 00000000000000000000.log
    
    再次插入一條key=null, value=”value”的訊息:
    -rw-r--r-- 1 root root       65 Apr 26 02:56 00000000000000000000.log
複製程式碼
kafka叢集訊息格式之V0版本到V2版本的平滑過渡詳解-kafka 商業環境實戰

總結:傳送每一條訊息必須攜帶12位元組LOG_OVERHEAD,是分散的訊息格式設計,沒有體現集合的味道。

5 V1(訊息戳進階)=> 22位元組+12(LOG_OVERHEAD)

kafka叢集訊息格式之V0版本到V2版本的平滑過渡詳解-kafka 商業環境實戰
  • kafka從0.10.0版本開始到0.11.0版本之前所使用的訊息格式版本為v1,其比v0版本就多了一個timestamp欄位,表示訊息的時間戳
kafka叢集訊息格式之V0版本到V2版本的平滑過渡詳解-kafka 商業環境實戰
  • 因此像v0版本介紹的一樣傳送一條key=”key”,value=”value”的訊息,那麼此條訊息在v1版本中會佔用42B

舉例如下:
傳送第一條key=”key”,value=”value”的訊息,則佔用22+12+8=42B
傳送第二條key=null,value=”value”的訊息,,則佔用12+22+5=39B

合在一起發則為:42+39=81B

5.1 V1集合(被V2Batch取代)

kafka叢集訊息格式之V0版本到V2版本的平滑過渡詳解-kafka 商業環境實戰

總結:傳送每一條訊息必須攜帶12位元組LOG_OVERHEAD,是分散的訊息格式設計,沒有體現集合的味道。

6 V2(變長整型與ZigZag) => 7個位元組+ 值key+ 值value =15位元組

  • kafka從0.11.0版本開始所使用的訊息格式版本為v2,這個版本的訊息相比於v0和v1的版本而言改動很大,同時還參考了Protocol Buffer而引入了變長整型(Varints)和ZigZag編碼。

  • Varints是使用一個或多個位元組來序列化整數的一種方法,數值越小,其所佔用的位元組數就越少。ZigZag編碼以一種鋸齒形(zig-zags)的方式來回穿梭於正負整數之間,以使得帶符號整數對映為無符號整數,這樣可以使得絕對值較小的負數仍然享有較小的Varints編碼值,比如-1編碼為1,1編碼為2,-2編碼為3。

  • zig-zags 會固定的將每一個位元組的第一位留作特殊用途,表明該位元組是否是最後一個位元組,若最高位是1,表示編碼尚未結束。因此實際上也僅有7位用於實際的編碼,即0-127。另外考慮 -1 ,1, -2, 2 對應 1,2, 3, 4。因此,0-63之間的數字佔1個位元組,64-8191之間的數字佔2個位元組,8192-1048575之間的數字佔3個位元組。kafka broker的配置message.max.bytes的預設大小為1000012(Varints編碼佔3個位元組)。

  • 注意的是Varints並非一直會省空間,一個int32最長會佔用5個位元組(大於預設的4位元組),一個int64最長會佔用10位元組(大於預設的8位元組)

kafka叢集訊息格式之V0版本到V2版本的平滑過渡詳解-kafka 商業環境實戰

總結 :v2版本的訊息格式去掉了crc欄位,另外增加了length(訊息總長度)、timestamp delta(時間戳增量)、offset delta(位移增量)和headers資訊,並且attributes被棄用。

6.1 V2 Record Batch => 61位元組+7位元組(純格式)+ 值key+ 值value =76位元組

v2版本對於訊息集(RecordBatch)做了徹底的修改,總共佔用了61個位元組,比如:把crc校驗放在了Batch這一層,冪等性引入,使用PID標識。epoch引入,標識當前版本。看似增大了訊息的容量大小,從大規模訊息來算的話,卻帶來了質的飛躍,因為一條純訊息格式僅佔用7位元組了,而V1佔用22位元組,V0佔用14位元組。

kafka叢集訊息格式之V0版本到V2版本的平滑過渡詳解-kafka 商業環境實戰
first offset:表示當前RecordBatch的起始位移。
length:計算partition leader epoch到headers之間的長度。
partition leader epoch:用來確保資料可靠性,詳細可以參考KIP-101
magic:訊息格式的版本號,對於v2版本而言,magic等於2。
attributes:訊息屬性,注意這裡佔用了兩個位元組。低3位表示壓縮格式,可以參考v0和v1;第4位表示時間戳型別;第5位表示此RecordBatch是否處於事務中,0表示非事務,1表示事務。第6位表示是否是Control訊息,0表示非Control訊息,而1表示是Control訊息,Control訊息用來支援事務功能。
last offset delta:RecordBatch中最後一個Record的offset與first offset的差值。主要被broker用來確認RecordBatch中Records的組裝正確性。
first timestamp:RecordBatch中第一條Record的時間戳。
max timestamp:RecordBatch中最大的時間戳,一般情況下是指最後一個Record的時間戳,和last offset delta的作用一樣,用來確保訊息組裝的正確性。
producer id:用來支援冪等性,詳細可以參考KIP-98。
producer epoch:和producer id一樣,用來支援冪等性。
first sequence:和producer id、producer epoch一樣,用來支援冪等性。
records count:RecordBatch中Record的個數。
複製程式碼

7 昇華

V1 版本的弊端:

  • 空間利用率低,key和value長度都各佔4個位元組,浪費。
  • 只保留最新位移,導致若獲取第一條訊息,就需要壓縮解壓縮後,遍歷出第一條資料。
  • 每一個訊息都會有CRC校驗。
  • 沒有訊息長度的概念。

V2 新型架構優點:

  • 增加訊息總長度
  • 保留時間戳,同時僅使用1個位元組來儲存,比如:10條訊息,V2需要100個位元組。V1就需要800個位元組了。
    -儲存位移增量。
  • 去除CRC校驗。
  • 廢除attribute,移到外層目錄。

總結

本篇技術專欄非常難寫,必須綜合大量資料和實驗才可以驗證。辛苦成文,各自珍惜。

秦凱新 於深圳

相關文章