(三)kafka高吞吐王牌殺手锏
A. 順序讀寫
-
影響因素
- 機(jī)械硬盤的io有兩個(gè)階段,分別為尋址 & 寫入。
- 尋址:物理動(dòng)作,通過旋轉(zhuǎn)和磁臂找到對(duì)應(yīng)扇區(qū),其動(dòng)作較慢,耗時(shí)是毫秒級(jí)。
- 寫入:數(shù)據(jù)的寫入階段很快,非優(yōu)化重點(diǎn)方向。
核心效果:producer傾倒消息時(shí)是不斷追加到文件的形式,因此kafka利用追加寫入完成磁盤順序讀寫,減少磁盤磁頭尋道時(shí)間,遠(yuǎn)快于隨機(jī)讀寫。
-
補(bǔ)充說明:linux io調(diào)度有4種方法
- NOOP(先進(jìn)先出的隊(duì)列)
- CFQ(默認(rèn)方法,根據(jù)io請(qǐng)求的地址排序,但是這導(dǎo)致小的io動(dòng)作如果地址靠后的話就需要一直等待)
- DEADLINE(CFQ的升級(jí)版,設(shè)置了等待時(shí)間的上限)
- ANTICIPATORY(每個(gè)io請(qǐng)求都等6ms,如果這個(gè)時(shí)間內(nèi)收到了地址相近的操作就合并一起)
B. 零拷貝zero copy
核心效果:基于linux的send file命令,減少內(nèi)核態(tài)到用戶態(tài)之間的拷貝。
-
具體流程
- 在內(nèi)核態(tài)kernel中操作,將數(shù)據(jù)從disk復(fù)制到memory buffer,在kafka的場景下,其實(shí)數(shù)據(jù)從disk走到了page cache;
- 從page cache中把數(shù)據(jù)傳遞到socket buffer,最后給到nic buffer發(fā)出去。
優(yōu)勢:上述兩步操作都在內(nèi)核態(tài)kernel中完成,若沒有send file機(jī)制數(shù)據(jù)需要從第一步的page cache拷貝到用戶態(tài)的kafka應(yīng)用中,然后再從kafka應(yīng)用中拷貝到內(nèi)核態(tài)的socket buffer中,多了兩次拷貝動(dòng)作。
C. 頁緩存page cache
- 核心效果:數(shù)據(jù)在內(nèi)存memory中的讀寫速度高于在磁盤disk下的讀寫速度,而page cache就是利用內(nèi)存空間來實(shí)現(xiàn)提速的。
- 具體流程:結(jié)合上方的zero copy,kafka完成了數(shù)據(jù)傳輸?shù)摹翱罩薪恿Α薄?
- 生產(chǎn)者將數(shù)據(jù)發(fā)送到broker;
- broker將數(shù)據(jù)先存至page cache,再刷入磁盤;
- 消費(fèi)者此時(shí)若拉取數(shù)據(jù),數(shù)據(jù)可基于send file,在broker上直接從page cache中到socket buffer,再從nic buffer送出;
- 消費(fèi)者憑借zero copy,結(jié)合page cache的加速更快獲得數(shù)據(jù)。
- 注意點(diǎn):kafka會(huì)用到大量memory作為page cache,所以linux的swap空間會(huì)被使用起來,一些不活躍的進(jìn)程被放入了swap。
D. 分區(qū)機(jī)制partition
1. 分區(qū)過多會(huì)破壞Kafka追加寫
- partition底層對(duì)應(yīng)的是一個(gè)或多個(gè)segment文件,若分區(qū)過多會(huì)導(dǎo)致有大量segment文件。雖然每個(gè)文件單獨(dú)看都是追加寫的模式,但是系統(tǒng)宏觀角度下會(huì)切換寫入多個(gè)segment文件,這樣尋址成本等于是隨機(jī)io(rocketMQ對(duì)此作了優(yōu)化,所有分區(qū)數(shù)據(jù)寫入一個(gè)commitLog)。
- 若kafka部署在云盤或者使用ssd就不用擔(dān)心該問題,前者走帶寬,后者不需要物理尋址。
2. Kafka數(shù)據(jù)存儲(chǔ)結(jié)構(gòu)
Topic & partition:Topic由大量partitions構(gòu)成,利用分布式結(jié)構(gòu)分散在不同broker節(jié)點(diǎn)上增加并行能力,而提升partition可以進(jìn)一步利用并發(fā)能力,隨著增加對(duì)應(yīng)下有消費(fèi)者數(shù)目,盡可能地提升吞吐量。
-
存儲(chǔ)的微觀單位:每個(gè)partition有單獨(dú)的目錄,目錄下又被具體分為多個(gè)segments,單個(gè)segment由一對(duì)文件——索引文件 & 數(shù)據(jù)文件。默認(rèn)情況下segment的數(shù)據(jù)文件大小為
log.segment.bytes=1073741824,即1gb。-
索引文件:0000000.index,其名稱為該段的起始索引號(hào),總體采用是稀疏索引的方式。索引文件由message索引號(hào)(從1開始)和該message的末尾offset組成。
Message index Message position annotation 1 0 第一條空的 3 4597 稀疏index,直接index=3 6 9807 -
數(shù)據(jù)文件:0000000.log,保存上方索引文件對(duì)應(yīng)的數(shù)據(jù)內(nèi)容,由數(shù)據(jù)和position組成。
message data Message position annotation Message1 data part 0 第一條空的 Message2 data part 2039 數(shù)據(jù)文件不可能稀疏,完整記載末尾的offset是多少 Message3 data part 4597 對(duì)應(yīng)index=3的情況 Message4 data part 6830 Message5 data part 7912 Message6 data part 9807 對(duì)應(yīng)index=6的情況
-
-
刪除機(jī)制
- 周期檢查:broker server周期性地檢測和刪除不符合保留條件的segments,具體周期根據(jù)配置的
log.retention.check.interval.ms參數(shù),默認(rèn)為5分鐘。 - 基于時(shí)間保留:基于配置的
log.retention.ms | log.retention.minutes | log.retention.hours(若都配置了,優(yōu)先級(jí)羅列的這個(gè)順序逐級(jí)降低),默認(rèn)168hours(7天)。非激活狀態(tài)下的segment超過這個(gè)時(shí)長后,會(huì)被清理。 - 基于文件大小保留:基于
log.retention.bytes的配置,目錄下所有segments的數(shù)據(jù)文件大小若大于這個(gè)值,則刪除最早的segment。
- 周期檢查:broker server周期性地檢測和刪除不符合保留條件的segments,具體周期根據(jù)配置的
E. 其他手段
- 批量發(fā)送:生產(chǎn)者客戶端緩存消息后批量發(fā)送給broker,消費(fèi)者也批量從broker拉取數(shù)據(jù),減少網(wǎng)絡(luò)上的io次數(shù)。
- 網(wǎng)絡(luò)瓶頸:kafka的核心性能瓶頸不是cpu、磁盤,而是網(wǎng)絡(luò)帶寬,需盡可能的壓縮數(shù)據(jù)(下個(gè)主題對(duì)kafka的網(wǎng)絡(luò)進(jìn)行了闡述說明)。