標籤:
在對訊息進行儲存和緩衝時,Kafka依賴於檔案系統。(Page Cache)
線性讀取和寫入是所有使用模式中最具可預計性的一種方式,因而作業系統採用預讀(read-ahead)和後寫(write-behind)技術對磁碟讀寫進行探測並最佳化後效果也不錯。預讀就是提前將一個比較大的磁碟塊中內容讀入記憶體,後寫是將一些較小的邏輯寫入操作合并起來組成比較大的物理寫入操作。
使用檔案系統並依賴於頁面緩衝(Page Cache)要優於自己在記憶體中維護一個緩衝或者什麼別的結構。
通過對所有空閑記憶體自動擁有訪問權,我們至少將可用的緩衝大小翻了一倍,然後通過儲存壓縮後的位元組結構而非單個對象,緩衝可用大小接著可能又翻了一倍。
這還大大簡化了代碼,因為對緩衝和檔案系統之間的一致性進行維護的所有邏輯現在都是在OS中實現的,這事OS做起來要比我們在進程中做那種一次性的緩衝更加高效,準確性也更高。
如果你使用磁碟的方式更傾向於線性讀取操作,那麼隨著每次磁碟讀取操作,預讀就能非常高效使用隨後准能用得著的資料填充緩衝。
資料被傳輸到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發出去
————————————————————————————————————————————————
Kafka訊息檔案儲存體