ISR機(jī)制讓我們可以增加或減少副本的數(shù)量,在可用性和性能之間做出權(quán)衡。可是擴(kuò)大或縮小副本的集合的副作用是增大了 丟失數(shù)據(jù) 的可能性。
DistributedLog使用的是Quorum投票復(fù)制算法,這在Zab、Raft以及Viewstamped Replication等一致性算法中都很常見。日志流的屬主會(huì)并發(fā)地把數(shù)據(jù)記錄寫入所有存儲(chǔ)節(jié)點(diǎn),并在得到超過配置數(shù)量的存儲(chǔ)節(jié)點(diǎn)投票確認(rèn)之后,才認(rèn)為數(shù)據(jù)已成功提交。存儲(chǔ)節(jié)點(diǎn)也只在數(shù)據(jù)被顯式地調(diào)用flush操作刷入磁盤之后才會(huì)響應(yīng)寫入請(qǐng)求。日志流的屬主也會(huì)維護(hù)一個(gè)日志流的最新提交的數(shù)據(jù)記錄的偏移量,就是大家知道的Apache BookKeeper中的LAC(LastAddConfirmed)。LAC也會(huì)保存在數(shù)據(jù)記錄中(來節(jié)省額外的RPC調(diào)用開銷),并不斷復(fù)制到別的存儲(chǔ)節(jié)點(diǎn)上。DistributedLog中復(fù)本集合的大小是在每個(gè)流的每個(gè)日志分片級(jí)別可配置的。改變復(fù)制參數(shù)只會(huì)影響新的日志分片,不會(huì)影響已有的。
存儲(chǔ)
每個(gè)Kafka分區(qū)都以若干個(gè)文件的形式保存在代理的磁盤上。它利用文件系統(tǒng)的頁緩存和I/O調(diào)度機(jī)制來得到高性能。Kafka也是因此利用Java的sendfile API來高效地從代理中寫入讀出數(shù)據(jù)的。不過,在某些情況下(比如消費(fèi)者處理不及時(shí)、隨機(jī)讀寫等),頁緩存中的數(shù)據(jù)淘汰很頻繁,它的性能也有很大的不確性性。
DistributedLog用的則是不同的I/O模型。圖三表示了Bookie(BookKeeper的存儲(chǔ)節(jié)點(diǎn))的I/O機(jī)制。寫入(藍(lán)線)、末尾讀(紅線)和中間讀(紫線)這三種常見的I/O操作都被隔離到了三種物理上不同的I/O子系統(tǒng)中。所有寫入都被順序地追加到磁盤上的日志文件,再批量提交到硬盤上。在寫操作持久化到磁盤上之后,它們就會(huì)放到一個(gè)Memtable中,再向客戶端發(fā)回響應(yīng)。Memtable中的數(shù)據(jù)會(huì)被異步刷新到交叉存取的索引數(shù)據(jù)結(jié)構(gòu)中:記錄被追加到日志文件中,偏移量則在分類賬目的索引文件中根據(jù)記錄ID索引起來。最新的數(shù)據(jù)肯定在Memtable中,供末尾讀操作使用。中間讀會(huì)從記錄日志文件中獲取數(shù)據(jù)。由于物理隔離的存在,Bookie節(jié)點(diǎn)可以充分利用網(wǎng)絡(luò)流入帶寬和磁盤的順序?qū)懭胩匦詠頋M足寫請(qǐng)求,以及利用網(wǎng)絡(luò)流出代寬和多個(gè)磁盤共同提供的IOPS處理能力來滿足讀請(qǐng)求,彼此之間不會(huì)相互干擾。

圖三:BookKeeper的I/O隔離
小結(jié)
Kafka和DistributedLog都是設(shè)計(jì)來處理日志流相關(guān)問題的。它們有相似性,但在存儲(chǔ)和復(fù)制機(jī)制上有著不同的設(shè)計(jì)理念,因此有了不同的實(shí)現(xiàn)方式。希望這篇文章能從技術(shù)角度解釋清楚它們的區(qū)別,回答一些問題。我們接下來也會(huì)再多寫一些文章來講講DistributedLog的性能指標(biāo)。