kafka日誌文件體系梳理

一些必須提前知道的概念

patition

kafka日誌文件是以patition在物理存儲上分割的

是topic物理上的分組,一個topic可以分爲多個partition,每個partition是一個有序的隊列

是以文件夾的形式存儲在具體Broker本機上

LEO

表示每個partition的log最後一條Message的位置

HW(HighWatermark)

表示partition各個replicas(用於分佈式存儲的副本分片)數據間同步且一致的offset位置,即表示all replicas已經commit的位置
HW之前的數據纔是Commit後的,對消費者纔可見
 

 

Segment

每個partition又由多個segment file組成
我們說的kafka日誌文件就是說的segment,下面我們主要討論一下segment文件的結構
 

Segment

日誌目錄以及文件組成

每個partition又由多個segment file組成
segment file 由2種文件組成組成,分別爲index file(索引文件)和log file(日誌文件)
 
日誌文件的文件後綴爲 *.log
索引文件的文件後綴爲 *.index、*.timeindex
 
命名規則:partition的第一個segment從0開始,後續每個segment文件名爲上一個segment文件最後一條消息的offset+1 (offset後文會提到)
 
下圖能比較清晰的反應出來partition、segment、和文件的關係,以及在分佈式系統中是如何體現的
 

從微觀即一個partition看:

消息主題是ZXP-TEST

此消息有3個partition(創建主題時指定),每個partition會有一個自己的目錄

每個partion的目錄下都會有segment文件,比如第一個segment文件由三個文件組成:00000000000000000000.log、00000000000000000000.index、00000000000000000000.timeindex

三個文件的作用如上圖,其中當log文件達到最大值,會新生成一套segment文件,文件都是追加寫,所以寫性能會比較高

我們再來從宏觀即3臺broker看:

broker1中partion ZXP-TEST-0是leader
broker1中partion ZXP-TEST-1是follower
broker1中partion ZXP-TEST-2是follower

那麼ZXP-TEST-0對應的follower在broker2、broker3(副本數由啓動參數offsets.topic.replication.factor決定)

ZXP-TEST-1對應的leader在broker2

注意:同一臺broker中不能存放同一個partition的主從分片,這點很多分佈式系統都有此設計

 

offset與position

offset偏移量:代表第幾個消息

position位置:代表消息在磁盤的物理位置

其中日誌文件命名中的偏移量是offset不是position,否則無法根據offset輕鬆找到在哪個日誌文件中

 

 

日誌文件內部數據結構

日誌索引文件

由offset、position組成

bin/kafka-run-class.sh kafka.tools.DumpLogSegments --files 00000000000000000000.index
# 也能印證日誌文件命名中的偏移量是offset不是position
offset:5083  position:1072592768 

索引文件是有序的,可以快速根據offset尋找position,position可以快速從log文件定位消息體

時間索引文件

 由timestamp、offset組成

bin/kafka-run-class.sh kafka.tools.DumpLogSegments --files 00000000000000000000.timeindex
timestamp: 1603703296169 offset: 5083

時間索引文件也是有序的

日誌文件

組成如下
X byte size
描述
8
POSITION
4
消息大小
4
CRC32 校驗值
1
magic kafka服務程序協議版本號
1
attributes 獨立版本或均表示壓縮類型、編碼類型
4
ley lenght key的長度,-1時key不存在
M
key key的內容,可選
4
payload length 消息體的長度
N
payload 消息數據
 
bin/kafka-run-class.sh kafka.tools.DumpLogSegments --files 00000000000000000000.log
baseoffset:5083  position: 1072592768  CreateTime: 1603703296169

 

 如何通過索引定位消息

 

找offset爲7的消息

1、首先是用二分查找確定它是在哪個LogSegment中,自然是在第一個Segment中(日誌文件命名中的偏移量是offset不是position)

2、打開這個Segment的index文件,也是用二分查找找到offset小於或者等於指定offset的索引條目中最大的那個offset。自然offset爲6的那個索引是我們要找的,通過索引文件我們知道offset爲6的Message在數據文件中的位置爲9807。(Kafka 中的索引文件,以稀疏索引(sparse index)的方式構造消息的索引,它並不保證每個消息在索引文件中都有對應的索引項)

3、打開數據文件,從位置爲9807的那個地方開始順序掃描直到找到offset爲7的那條Message(找到第6個消息後根據position+消息大小向後找直到找到)
 

與rocketMQ的對比

 
rocketmq
kafka
消費的offset記錄
第幾個消息,保存於offsetTable.offset json文件
第幾個消息,保存於__consumer_offsets內置主題中
索引文件
ConsumeQueue、(數組、下標就是offset,每行的offset其實是position)
indexFile
index文件、(追加的,每條定長,offset、position)
timeindex
日誌文件
CommitLog(未具體研究,應該差不多)
log文件結構見上文
 

關於rocketmq的日誌存儲可以參考我的另一篇文章《快速弄明白RocketMQ的CommitLog、ConsumeQueue、indexFile、offsetTable 以及多種偏移量對比》

 

 

日誌文件清理

Kafka將數據持久化到了硬盤上,爲了控制磁盤容量,需要對過去的消息進行清理(segment文件)

清理策略

1、內部有個定時任務檢測刪除日誌,默認是5分鐘 log.retention.check.interval.ms

2、支持配置策略對數據清理

3、根據segment單位進行定期清理

log.cleaner.enable=true
log.cleaner.threads = 2 (清理線程數配置)
log.cleanup.policy=delete

#清理超過指定時間的消息,默認是168小時,7天,
#還有log.retention.ms, log.retention.minutes, log.retention.hours,優先級高到低
log.retention.hours=168​
#超過指定大小後,刪除舊的消息,下面是1G的字節數,-1就是沒限制
log.retention.bytes=1073741824

  

基於【時間刪除】 日誌說明

每個日誌段文件都維護一個最大時間戳字段,每次日誌段寫入新的消息時,都會更新該字段
一個日誌段segment寫滿了被切分之後,就不再接收任何新的消息,最大時間戳字段的值也將保持不變
kafka通過將當前時間與該最大時間戳字段進行比較,從而來判定是否過期

基於【大小超過閾值】 刪除日誌 說明

假設日誌段大小是500MB,當前分區共有4個日誌段文件,大小分別是500MB,500MB,500MB和10MB
10MB那個文件就是active日誌段。
此時該分區總的日誌大小是3*500MB+10MB=1500MB+10MB
如果閾值設置爲1500MB,那麼超出閾值的部分就是10MB,小於日誌段大小500MB,故Kafka不會執行任何刪除操作,即使總大小已經超過了閾值;
如果閾值設置爲1000MB,那麼超過閾值的部分就是500MB+10MB > 500MB,此時Kafka會刪除最老的那個日誌段文件
注意:超過閾值的部分必須要大於一個日誌段的大小纔會刪除
 
刪除日誌時不考慮,是不是還有未消費的數據,對於一個topic隨時也有可能增加新的消費者

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章