1、文件目錄布局
????????不考慮多副本的情況蹋订,一個分區(qū)對應(yīng)一個日志(log)率挣。為了防止log過大,Kafka還引入了日志分段(LogSegment)的概念露戒,將log切分為多個LogSegment椒功。
????????Log對應(yīng)了一個命名形式為<topic>-<partition>的文件夾。
????????向Log中追加消息時時順序?qū)懭氲闹鞘玻挥凶詈笠粋€LogSegment才能執(zhí)行寫入操作荠锭。
????????為了方便消息的檢索,每個LogSegment中的日志文件删豺,以“.log”為文件后綴的都有兩個索引文件:偏移量索引文件(“.index”)和時間戳索引文件(".timeindex")愧怜。每個LogSegment都有一個基準偏移量baseOffset叫搁,用來表示當前LogSegment中第一條消息的offset。
2疾党、日志索引
????????偏移量索引文件用來建立消息索引量(offset)和物理地址之間的映射關(guān)系惨奕,方便快速定位消息所在的物理文件位置。時間戳索引文件根據(jù)指定的時間戳來查找對應(yīng)的偏移量信息雹洗。
? ? ? ? ? Kafka的索引文件以稀疏索引的方式構(gòu)建消息的索引时肿,它并不保證每個消息在索引文件中都有對應(yīng)的索引項港粱。每當寫入一定量的消息時,偏移量索引文件和時間戳索引文件分別增加一個偏移量索引項和時間戳索引項寸宏,增大或減少log.index.interval.bytes的值偿曙,對應(yīng)地可以縮小或增加索引項的密度。
????????稀疏索引的方式是在磁盤空間罩阵、內(nèi)存空間永脓、查找時間等多方面之間的一個折中鞋仍,使用二分法來快速定位偏移量的位置。????????
2.1落午、偏移量索引
????????每個索引量占據(jù)8個字節(jié)溃斋。relativeOffset是相對偏移量梗劫,表示消息相對于baseOffset的偏移量,占據(jù)4個字節(jié)梳侨。position是物理地址,也就是消息在日志分段文件中對應(yīng)的物理位置蚯嫌。
????????沒有使用絕對偏移量而使用相對偏移量择示,是為了節(jié)省索引文件所占的空間晒旅。絕對偏移量占8和字節(jié),相對偏移量占用4個字節(jié)剪菱。
????????例如:如何查找偏移量是268的消息拴签。首先是定位到baseOffset為251的日志分段蚓哩,然后計算出relativeOffset=268-251=17,之后再在對應(yīng)的索引文件中找到不大于17的索引項喜颁,最后找到根據(jù)索引項中的position定位到具體的日志分段文件位置開始查找目標消息曹阔。那么如何查找baseOffset為251的日志分段呢?這里不是順序查找寂拆,而是使用了跳躍表的結(jié)構(gòu)抓韩。
2.2、時間戳索引
????????timestamp:時間戳索引尝江,當前日志分段最大的時間戳炭序。
????????relativeOffset:時間戳所對應(yīng)的消息的相對偏移量。
????????每當消息寫入一定量的時候惭聂,就會在偏移量索引和時間戳索引文件中分別增加一個偏移量索引項和時間戳索引。兩個文件增加的索引項的操作是同時進行的嫌佑。
????????如果要查找timeStamp=15682185885開始的消息侨歉,首先是找到不小于指定時間戳的日志分段揩魂。這里無法再使用跳躍表定位到對應(yīng)的日志分段了火脉。
????????步驟1:將target和每個日志分段中的最大時間戳largestTimeStamp逐一對比,直到找到不小于target的多對應(yīng)的日志分段倦挂。
????????步驟2:找到對應(yīng)的日志分段后方援,在時間戳索引文件中使用二分查找算法找到對應(yīng)的偏移量。
????????步驟3:在偏移量索引文件中使用二分查找到不大于此的索引量犯戏。
????????步驟4:從步驟1中找到日志分段文件中的838的物理位置開始查找不小于target的消息。
3种吸、日志清理
????????Kafka將消息存儲在磁盤中坚俗,為了控制磁盤所占空間的不斷增加就需要對消息做一定的清理岸裙。兩種策略:
????????日志刪除:按照一定的保留策略直接刪除不符合條件的日志分段。三種辙浑,基于時間拟糕、基于日志大小倦踢、基于日志起始偏移量辱挥。
????????日志壓縮:針對每個消息的key進行整合边涕,對于有相同key的不同value值,只保留最后一個版本园爷。
????????通過設(shè)置broker端的參數(shù)log.cleanup.policy來設(shè)置日志清理策略童社,默認值delete著隆,日志壓縮是compact∠依担可以同時啟用兩種方式浦辨,“delete,compact”。
4案腺、磁盤存儲
????????Kafka依賴磁盤來做存儲和緩存消息康吵。在我們的印象中晦嵌,磁盤處于一個尷尬的位置,不是很快旱函。在傳統(tǒng)的消息中間件RabbitMQ中棒妨,就使用內(nèi)存作為默認的存儲介質(zhì)。然而券腔,事實上磁盤可以比我們預(yù)想的要快,也可能比我們預(yù)想的要慢枕扫,這取決于我們怎么使用它辱魁。
????????一項研究表明,磁盤的順序?qū)懭胨俣群碗S機寫入速度相差6000倍参滴。操作系統(tǒng)可以針對線性讀寫做深層次的優(yōu)化卵洗,比如預(yù)讀和后寫请唱。順序?qū)懕P的速度不僅比隨機寫盤的速度快,而且比隨機寫內(nèi)存的速度也快聚至。
????????Kafka在設(shè)計時采用了文件追加的方式來寫入消息本橙,即只能在日志文件的尾部追加新的消息,并且也不允許修改以寫入的消息贷币,這是屬于典型的順序?qū)懕P的操作役纹。
4.1暇唾、頁緩存
????????頁緩存是操作系統(tǒng)實現(xiàn)的一種主要的磁盤緩存,以此來減少磁盤I/O的操作瘸味。具體來說旁仿,就是把磁盤中的數(shù)據(jù)緩存到內(nèi)存中孽糖,把對磁盤的訪問變?yōu)閷?nèi)存的訪問汁胆。
????????當一個進程準備讀寫磁盤上的文件內(nèi)容時嫩码,操作系統(tǒng)會先查看讀取的數(shù)據(jù)所在的頁(page)是否在頁緩存(pagecache)中铸题,如果命中則直接返回數(shù)據(jù)琢感,從而避免了對物理磁盤的I/O操作驹针。如果沒有命中,則操作系統(tǒng)會向磁盤發(fā)起讀取請求并將讀取的數(shù)據(jù)頁存入頁緩存中饮六,之后再將數(shù)據(jù)返回給進程卤橄。同樣臂外,如果一個進程需要將數(shù)據(jù)寫入磁盤漏健,那么操作系統(tǒng)也會將檢測數(shù)據(jù)對應(yīng)的頁是否在頁緩存中,如果不存在殖属,則會先在頁緩存中添加相應(yīng)的頁克懊,最后將數(shù)據(jù)寫入頁對應(yīng)的頁谭溉。被修改后的頁也就變成了臟頁,操作系統(tǒng)會在合適的時間將臟頁中的數(shù)據(jù)寫入磁盤扮念,以保持數(shù)據(jù)的一致性。
????????Kafka中大量使用了頁緩存嵌灰,這是Kafka實現(xiàn)高吞吐的重要因素之一沽瞭。
4.2剩瓶、零拷貝
????????除了消息順序追加、頁緩存等技術(shù)豌鹤,Kafka還使用零拷貝(Zero-Copy)技術(shù)來進一步提升性能布疙。
????????所謂的零拷貝是指將數(shù)據(jù)直接從磁盤文件復(fù)制到網(wǎng)卡設(shè)備中灵临,而不需要經(jīng)由應(yīng)用程序之手擦酌。零拷貝大大提高了應(yīng)用程序的性能菠劝,減少了內(nèi)核和用戶模式之間的上下文切換赶诊。