在對消息進行存儲和緩存時,Kafka依賴於文件系統。(Page Cache)java
線性讀取和寫入是全部使用模式中最具可預計性的一種方式,於是操做系統採用預讀(read-ahead)和後寫(write-behind)技術對磁盤讀寫進行探測並優化後效果也不錯。預讀就是提早將一個比較大的磁盤塊中內容讀入內存,後寫是將一些較小的邏輯寫入操做合併起來組成比較大的物理寫入操做。緩存
使用文件系統並依賴於頁面緩存(Page Cache)要優於本身在內存中維護一個緩存或者什麼別的結構。服務器
經過對全部空閒內存自動擁有訪問權,咱們至少將可用的緩存大小翻了一倍,而後經過保存壓縮後的字節結構而非單個對象,緩存可用大小接着可能又翻了一倍。異步
這還大大簡化了代碼,由於對緩存和文件系統之間的一致性進行維護的全部邏輯如今都是在OS中實現的,這事OS作起來要比咱們在進程中作那種一次性的緩存更加高效,準確性也更高。socket
若是你使用磁盤的方式更傾向於線性讀取操做,那麼隨着每次磁盤讀取操做,預讀就能很是高效使用隨後準能用得着的數據填充緩存。佈局
數據被傳輸到OS內核的頁面緩存中了,OS隨後會將這些數據刷新到磁盤的。此外咱們添加了一條基於配置的刷新策略,容許用戶對把數據刷新到物理磁盤的頻率進行控制(每當接收到N條消息或者每過M秒),從而能夠爲系統硬件崩潰時「處於危險之中」的數據在量上加個上限。性能
——————————————————————————————————————————————————優化
【與BTree方式對比】操作系統
持久化隊列能夠按照一般的日誌解決方案的樣子構建,只是簡單的文件讀取和簡單地向文件中添加內容。線程
雖然這種結果必然沒法支持BTree實現中的豐富語義,但有個優點之處在於其全部的操做的複雜度都是O(1),讀取操做並不須要阻止寫入操做,並且反之亦然。
這樣作顯然有性能優點,由於性能徹底同數據大小之間脫離了關係 —— 一個服務器如今就能利用大量的廉價、低轉速、容量超過1TB的SATA驅動器。雖然這些驅動器尋道操做的性能很低,但這些驅動器在大量數據讀寫的狀況下性能還湊和,而只需1/3的價格就能得到3倍的容量。 可以存取到幾乎無限大的磁盤空間而無須付出性能代價意味着,咱們能夠提供一些消息系統中並不常見的功能。例如,在Kafka中,消息在使用完後並無當即刪除,而是會將這些消息保存至關長的一段時間(比方說一週)。
——————————————————————————————————————————————————
Kafka的存儲佈局很是簡單。話題的每一個分區對應一個邏輯日誌。物理上,一個日誌爲相同大小的一組分段文件。每次生產者發佈消息到一個分區,代理就將消息追加到最後一個段文件中。當發佈的消息數量達到設定值或者通過必定的時間後,段文件真正寫入磁盤中。寫入完成後,消息公開給消費者。
與傳統的消息系統不一樣,Kafka系統中存儲的消息沒有明確的消息Id。
消息經過日誌中的邏輯偏移量來公開。這樣就避免了維護配套密集尋址,用於映射消息ID到實際消息地址的隨機存取索引結構的開銷。消息ID是增量的,但不連續。要計算下一消息的ID,能夠在其邏輯偏移的基礎上加上當前消息的長度。
消費者始終從特定分區順序地獲取消息,若是消費者知道特定消息的偏移量,也就說明消費者已經消費了以前的全部消息。消費者向代理髮出異步拉請求,準備字節緩衝區用於消費。每一個異步拉請求都包含要消費的消息偏移量。Kafka利用sendfile API高效地從代理的日誌段文件中分發字節給消費者。
——————————————————————————————————————————————————
————————————————————————————————————————————————
【Kafka高效文件存儲設計特色】
Kafka把topic中一個parition大文件分紅多個小文件段,經過多個小文件段,就容易按期清除或刪除已經消費完文件,減小磁盤佔用。
經過索引信息能夠快速定位message和肯定response的最大大小。
經過index元數據所有映射到memory,能夠避免segment file的IO磁盤操做。
經過索引文件稀疏存儲,能夠大幅下降index文件元數據佔用空間大小。
————————————————————————————————————————————————
Partition:topic物理上的分組,一個topic能夠分爲多個partition,每一個partition是一個有序的隊列。
Segment:partition物理上由多個segment組成,下面2.2和2.3有詳細說明。
offset:每一個partition都由一系列有序的、不可變的消息組成,這些消息被連續的追加到partition中。partition中的每一個消息都有一個連續的序列號叫作offset,用於partition惟一標識一條消息.
————————————————————————————————————————————————
【kafka文件存儲機制】
分析過程分爲如下4個步驟:
topic中partition存儲分佈
partiton中文件存儲方式
partiton中segment文件存儲結構
在partition中如何經過offset查找message
————————————————————————————————————————————————
partiton中文件存儲方式
每一個partion(目錄)至關於一個巨型文件被平均分配到多個大小相等segment(段)數據文件中。但每一個段segment file消息數量不必定相等,這種特性方便old segment file快速被刪除。
每一個partiton只須要支持順序讀寫就好了,segment文件生命週期由服務端配置參數決定。
這樣作的好處就是能快速刪除無用文件,有效提升磁盤利用率。
————————————————————————————————————————————————
【partiton中segment文件存儲結構】
讀者從2.2節瞭解到Kafka文件系統partition存儲方式,本節深刻分析partion中segment file組成和物理結構。
segment file組成:由2大部分組成,分別爲index file和data file,此2個文件一一對應,成對出現,後綴".index"和「.log」分別表示爲segment索引文件、數據文件.
segment文件命名規則:partion全局的第一個segment從0開始,後續每一個segment文件名爲上一個segment文件最後一條消息的offset值。數值最大爲64位long大小,19位數字字符長度,沒有數字用0填充。
下面文件列表是筆者在Kafka broker上作的一個實驗,建立一個topicXXX包含1 partition,設置每一個segment大小爲500MB,並啓動producer向Kafka broker寫入大量數據,以下圖2所示segment文件列表形象說明了上述2個規則:
————————————————————————————————————————————————
2.4 在partition中如何經過offset查找message
例如讀取offset=368776的message,須要經過下面2個步驟查找。
第一步查找segment file
上述圖2爲例,其中00000000000000000000.index表示最開始的文件,起始偏移量(offset)爲0.第二個文件00000000000000368769.index的消息量起始偏移量爲368770 = 368769 + 1.一樣,第三個文件00000000000000737337.index的起始偏移量爲737338=737337 + 1,其餘後續文件依次類推,以起始偏移量命名並排序這些文件,只要根據offset **二分查找**文件列表,就能夠快速定位到具體文件。
當offset=368776時定位到00000000000000368769.index|log
第二步經過segment file查找message
經過第一步定位到segment file,當offset=368776時,依次定位到00000000000000368769.index的元數據物理位置和00000000000000368769.log的物理偏移地址,而後再經過00000000000000368769.log順序查找直到offset=368776爲止。
從上述圖3可知這樣作的優勢,segment index file採起稀疏索引存儲方式,它減小索引文件大小,經過mmap能夠直接內存操做,稀疏索引爲數據文件的每一個對應message設置一個元數據指針,它比稠密索引節省了更多的存儲空間,但查找起來須要消耗更多的時間。
————————————————————————————————————————————————
從上述圖5能夠看出,Kafka運行時不多有大量讀磁盤的操做,主要是按期批量寫磁盤操做,所以操做磁盤很高效。
這跟Kafka文件存儲中讀寫message的設計是息息相關的。Kafka中讀寫message有以下特色:
寫message
消息從java堆轉入page cache(即物理內存)。
由異步線程刷盤,消息從page cache刷入磁盤。
讀message
消息直接從page cache轉入socket發送出去。
當從page cache沒有找到相應數據時,此時會產生磁盤IO,從磁
盤Load消息到page cache,而後直接從socket發出去
————————————————————————————————————————————————