Ledgers和Fragments是在Zookeeper中維護(hù)和跟蹤的邏輯結(jié)構(gòu)。物理上數(shù)據(jù)不存儲(chǔ)在Ledgers和Fragments對(duì)應(yīng)的文件中。BookKeeper中存儲(chǔ)的實(shí)現(xiàn)是可拔插的,Pulsar默認(rèn)使用名稱為DbLedgerStorage的存儲(chǔ)實(shí)現(xiàn)。
Pulsar 的數(shù)據(jù)最終是靠 Bookkeeper(Bookie) 存儲(chǔ)的,各個(gè) Pulsar Bookie 之間是平等的,kafka 的存儲(chǔ)節(jié)點(diǎn)在 Partition 層次上有主從之分。
pulsar單個(gè) Broker 數(shù)據(jù)寫流程如下:
- 1 將寫請(qǐng)求記入 WAL【類似于數(shù)據(jù)庫的 Journal 文件】
一般把 WAL 和數(shù)據(jù)存儲(chǔ)文件分別存儲(chǔ)到兩種存儲(chǔ)盤上,如把 WAL 存入一個(gè) SSD 盤,而數(shù)據(jù)文件存入另一個(gè) SSD 或者 SATA 盤。
2 將數(shù)據(jù)寫入內(nèi)存緩存中。
3 寫緩存寫滿后或達(dá)到時(shí)間閾值時(shí),進(jìn)行數(shù)據(jù)排序并刷盤,排序時(shí)將同一個(gè)ledger的數(shù)據(jù)聚合后按照時(shí)間先后進(jìn)行排序。
4 將 <(LedgerID, EntryID), EntryLogID> 寫入 RocksDB
LedgerID 相當(dāng)于 kafka 的 ParitionID,EntryID 即是 Log Message 的邏輯 ID,EntryLogId 就是 Log消息在 Pulsar Fragment文件的物理 Offset。
這里把這個(gè)映射關(guān)系存儲(chǔ) RocksDB 只是為了加快寫入速度,其自身并不是 Pulsar Bookie 的關(guān)鍵組件。

讀:
1 從寫緩存讀取數(shù)據(jù)【因?yàn)閷懢彺嬗凶钚碌臄?shù)據(jù)】;
2 如果寫緩存不命中,則從讀緩存讀取數(shù)據(jù);
3 如果讀緩存不命中,則根據(jù) RocksDB 存儲(chǔ)的映射關(guān)系查找消息對(duì)應(yīng)的物理存儲(chǔ)位置,然后從磁盤上讀取數(shù)據(jù);
4 把從磁盤讀取的數(shù)據(jù)回填到讀緩存中;
5 把數(shù)據(jù)返回給 Broker。
寫:
Bookie 的整個(gè)寫入流程除了自身把內(nèi)存緩存數(shù)據(jù)批量刷盤一步外,整個(gè)流程幾乎不需要跟磁盤進(jìn)行IO,所以速度也是極快。
寫入都按順序?qū)懭肴罩疚募梢源鎯?chǔ)在專用的磁盤上,并且可以批量刷盤以獲得搞得吞吐量。除此之外從寫入操作來看沒有其他的同步磁盤IO操作,數(shù)據(jù)都是寫入到內(nèi)存的緩存區(qū)。
總結(jié)

上圖為Pulsar默認(rèn)使用的bookie架構(gòu)