BookKeeper淺析

序言,關於cap

CAP是分佈式系統中的一個特別重要的理論。

在這裏插入圖片描述

CAP原則又稱CAP定理,指的是在一個分佈式系統中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分區容錯性),三者不可得兼。CAP是NOSQL數據庫的基石。

分佈式系統的CAP理論:理論首先把分佈式系統中的三個特性進行了如下歸納:

  • 一致性(C):在分佈式系統中的所有數據備份,在同一時刻是否同樣的值。(等同於所有節點訪問同一份最新的數據副本)
  • 可用性(A):在集羣中一部分節點故障後,集羣整體是否還能響應客戶端的讀寫請求。(對數據更新具備高可用性)
  • 分區容忍性(P):以實際效果而言,分區相當於對通信的時限要求。系統如果不能在時限內達成數據一致性,就意味着發生了分區的情況,必須就當前操作在C和A之間做出選擇。

關於分區容錯性,需要解釋一下:所謂分區指的是網絡分區的意思,詳細一點解釋,比如你有A B兩臺服務器,它們之間是有通信的,突然,不知道爲什麼,它們之間的網絡鏈接斷掉了。那麼現在本來AB在同一個網絡現在發生了網絡分區,變成了A所在的A網絡和B所在的B網絡。所謂的分區容忍性,就是說一個數據服務的多臺服務器在發生了上述情況的時候,依然能繼續提供服務。

如果不能滿足p,就不能再網絡分區的情況下保證服務,那麼就是單個節點的情況,和分佈式系統的設計初衷是背離的。所以討論cap是,一般都是滿足p的前提下,來選在滿足a還是滿足c。

如果滿足C,即所有節點都需要有相同的數據,如果沒有就不可服務,滿足不了A;

如果滿足A,即服務可以一直存在,那麼在節點故障等場景下,就滿足不了C。

BookKeeper是滿足CP特性的分佈式系統,並且同時提供了較高的可用性,下文會有論述。

BookKeeper是企業級的存儲系統,提供強持久性、一致性和低延遲的保證。最初起源於Yahoo!,用來解決HDFS NN的單點問題。2011年作爲ZooKeeper的子項目在Apache孵化,2015成爲頂級項目。

BookKeeper

一個企業級、實時的存儲平臺需要滿足一下要求:

  • 讀寫低延遲(< 5ms)
  • 數據存儲要持久、一致並且支持容錯
  • 對寫入數據提供流式傳播或者tail傳播的功能
  • 高效的存儲,可以提供對實時數據以及歷史數據的訪問

BK是一個可擴展、支持容錯並且可以保證低延遲的存儲服務。滿足了以上的要求,適用於很多場景:

  • 爲分佈式存儲提供高可用/副本機制(HDFS NN,Manhattan(KV store in Twitter))
  • 在單個集羣或者多個集羣之間提供複製功能
  • 爲訂閱/發佈系統提供存儲能力(EvnentBus in Twitter and Apache pulsar)
  • 爲流式作業存儲不可變對象(比如checkpoint 數據快照)

BK概念和術語

Records(Entry)

數據在BK以record的形式而非bytes的形式寫入BK。Record是最小的I/O單位,也是地址單元。每個record都包含了一個序列號(單調遞增的long性數)。Client從某一個record開始讀取數據,或者tail一個序列,即監聽添加到log的下一個record。可以單條或者批量接收record。序列號也可以用來隨機檢索record。

Entry除了包含寫入bookie的實際數據之外,還包含一些元數據信息

字段 說明 類型
Ledger Number Entry寫入的ledger ID long
Entry number Entry的唯一ID long
Last confirmed (LC) 最後記錄的Entry ID long
Date 數據 byte[]
Authentication code 鑑權數據 which includes all other fields in the entry byte[]

Logs(BK的存儲)

BK爲提供了兩種存儲原語:

  • ledger,即 log segment
  • stream ,即 log stream

一個Ledger是數據records的序列,ledger在客戶端顯式的關閉或者writer寫入失敗時終止。一旦一個ledger 終止以後,就不能append記錄。ledger是最底層的存儲原語,可以用於存儲有限的序列或者無限的流。

在這裏插入圖片描述

Ledgers是entry的序列,Entry是bytes序列。Entry寫入ledger時,是

  • 順序的
  • at most once

ledgers支持 append-only的語義。Entry寫入到ledgers之後,就不可以被修改了。Client應用決定寫入的順序。

一個stream是無界的,無限的record序列。Leger可以被打開多次來append record。一個stream物理上會有多個ledger組成,每個ledger會根據時間或者空間滾動策略來滾動。stream會存在很長時間,所以保留數據策略是truncating,會根據時間或者空間策略來丟棄老的數據。
在這裏插入圖片描述

ledge和stream爲歷史數據或者實時數據提供了統一的存儲抽象。Log streams提供了數據tail或者stream能力。實時數據存儲在ledger中,隨着時間會變成歷史數據。

NameSpackes

Log stream通常在namespacke下分類、管理。NameSpace是租戶創建流時會使用到的一種機制。一個namespace是數據存儲策略的部署和管理單元。在同一個namespace下的所有stream繼承相同的配置,數據存儲在存儲策略配置的存儲節點上。可以方便的管理過個stream。

Bookie

BK在多個server之間備份存儲數據entry,這些server成爲Bookie。Bookie是個獨立的Bk存儲服務。

Bookies是處理ledgers(更具體一些,是ledger的片段)的server。Bookies作爲ensemble的一部分起作用

一個bookie是一個獨立的BookKeeper存儲服務。出於性能的考慮,Bookie儲存ledger的片段,而不是真個ledgers。對於任意的一個ledger, ensemble是存儲這個ledger entries的bookies組。entry寫入ledger時,會在ensemble中進行條帶化的存儲。

向ledger寫入entry時,entry會被條帶化到ensemble中(bookie的子集不是全部)。

Metadata

BK的元數據主要是可用的BK以及ledger信息。使用ZK存儲。

與BookKeeper交互

BookKeeper客戶端主要有兩個角色:創建和刪除ledgers,向ledgers寫入或者從ledgers中讀取entry。

主要兩種方式和BK交互:

  • 創建ledger或者stream來寫數據
  • 打開ledger或者stream來讀數據

BK提供了兩種API:

  • ledger API: 底層API,可以直接操作ledger,比較靈活。
  • Stream API: higher-level面向流的API,通過Apache DistributedLog提供,直接操作流,不用關心與ledger交互的複雜性。

選擇哪種API,應該基於應用對於ledger的控制粒度。兩種API可以在應用中共同使用。

BookKeeper 框架圖

在這裏插入圖片描述

注意:

  • BK的典型安裝會包含:Metadata store,bookie cluster,多個cilent
  • Bookie自己向metadata store註冊自己
  • Bookie在進行GC來清理數據時需要和metadata store交互
  • 應用程序使用Client lib來和BK交互
    • 使用Ledger API獲取細粒度的管控
    • 不需要底層ledger控制,則使用stream api

BookKeeper提供的保證

前文中提到了一個實時儲存系統需要滿足的一些要求,總結如下:

Guarantee Description
Replication 數據在多臺機器上都有持久存儲的副本存在
Durability 數據寫入時,在ack之前需要寫fsync到磁盤
Consistency 提供 repeatable read的一致性
Avaliablity 寫:ensemble可以切換;讀:提供了speculative read
Low Latency 讀寫I/O隔離

Replication

BK會爲每個記錄都複製並且存儲幾個獨立的備份,通常是3或者5,備份可以位於相同或者不同數據中心。不同於其他的HA系統,使用了主/備複製或者流水線的算法來同步數據,BK使用 quorum-vote parallel replication algorithm,這種算法保證了可預測的數據複製延遲。下圖描述了BK內部的數據複製:
在這裏插入圖片描述

上圖中:

  1. 從BK集羣中選取bookie集合。這個集合作爲ledger存儲記錄的 ensemble
  2. Ledger的record條帶化的存儲在ensemble的bookie上。也就是說,麼個record都會存儲在一定數量的副本上。這個數量,可以通過client配置,成爲write quorum size。上圖中的quorum是3, record會寫到bookie 2,3,4上。
  3. 當一個client寫如數據到ensemble時,client會等待一定使用量的ack。Ack的數量成爲ack quorum size。當ack的數量達到要求時,客戶端纔會認爲寫入操作成功。上圖中的ack quorum是2,即客戶端收到2個ack即可認爲數據寫入成功。
  4. Ensemble在bookie失敗時會有變更。失敗的bookie會被替換掉。比如bookie x替換掉bookie5。

Replication: core ideas

BK複製的核心思想:

  1. Log stream是面向record的,而不是面向bytes的。即數據總是以不可分割的record的形式存在,而不是字節數組。
  2. Log stream 中record的順序和實際副本中存儲的record的順序是解耦的(實際日誌文件是多個ledger共享的,record寫入日誌之前會根據多個ledger排序,排序之後寫入文件)

這兩個原則使得 BookKeeper replication 可以:

  • 提供寫record到bookie的多種選擇,只要BK有足夠的容量就可以保證即使在許多bookie宕機或者響應慢的情況下依然可以寫入成功,這是會涉及到ensemble的變更
  • 通過增加ensemble的大小,增加單個Log stream的帶寬,一個單獨的log就不會限制到一臺或者幾臺機器上。可以通過配置ensemble大小大於write quorum size來實現。
  • 可以通過調整ack quorum大小的值來提升延遲性能,這個配置在保證一致性和持久性的前提下獲取低延遲的關鍵。
  • 通過many-to-many的複製恢復提供快速的re-replication (re-replication 爲under-replicated(小於write quorum size)狀態的record創建 more replicas ). 所有的bookie都可以作爲record副本的提供者和接收者。

Durability

每個數據record寫入BK時,都會保證被複制並且持久的存儲在一定數量的bookie上。這種保證是通過顯式的disk fsync和寫入確認來實現的。

  1. 在單個bookie上,data record在ack之前會被顯式的寫入磁盤(fsynced)。
  2. 在一個集羣內,爲了容錯,數據record會被複制到多個bookie上。
  3. 數據record只有在客戶端收到一定數量(配置的)bookie的響應之後纔會ack。

大多數的NoSQL類型的數據庫,分佈式文件系統以及消息系統(比如Kafka),都假設將數據複製到多個node的易失存儲/內存就已經足夠滿足 best-effort 持久性要求,但是這些系統允許潛在的數據丟失。BK設計用來提供更強的持久性保證,可以完全避免數據丟失。

Consistency

一致性保證是分佈式系統的常見問題,尤其是在引入複製機制來提供持久性和可用性時。BK提供了一個簡單但是強大的一致性保證——可重複讀 :

  • 如果一個record已經ack,那麼這個record必須立即可讀。
  • 如果一個record被讀過一次,那麼這個record必須總是可讀的。
  • 如果一個record R寫入成功,那麼所有的在R之前的記錄都是成功提交/持久化的並且是可讀的。
  • 數據存儲的順序對於多有的reader來講必須都是一樣的。

可重複讀一致性是通過LAC協議保證的。

Availability

在一個CAP理論體系中,BK是個CP系統。實際上,即使在硬件、網絡或者其他失敗的情況下,BK依舊提供了非常高的可用性。BK使用了一下的機制,爲了保證讀寫的高可用性:

Availability type Mechanism Description
Write availability Ensemble changes 在bookkie出現失敗時,client會重新配置寫入的bookie。這就保證了在有足夠的bookie時,寫入總會成功。
Read availability Speculative reads This helps spread read traffic across bookies, which has the added benefit of reducing tail read latency. 和其他的系統只從指定的leader節點讀數據不一樣,BK允許客戶端從任何ensemble的任何一個bookie讀取record。這種機制可以將讀取分散到多個bookie上,相比於其他系統,在降低及時消費的延遲方面有額外的優勢。

Low latency

強持久性和一致性保證是分佈式系統中的複雜問題,尤其是要滿足企業級的低延遲。BK可以完全滿足這些要求:

  • 在單個bookie上,bookie服務爲不同的workload(寫、tailing read,追趕讀/隨機讀)類型提供了I/O隔離。Journal文件使用 group-committing mechanism 來均衡延遲和吞吐。
  • quorum-vote parallel replication scheme 用來屏蔽網絡故障、JVM GC停頓、磁盤慢等導致的延遲問題。這可以減少tail 延遲並且可以確保99%的可預測的低延遲。
  • 一個長輪訓機制來notify並且分發新寫入的record給tailing readers(一旦record ack並且confirmed)。

最終,需要指出,顯式的fsync帶來的持久性、寫入confirm以及重複讀的一致性是狀態處理的關鍵,尤其是對於流應用中的effectively-once處理。

I/O 隔離

可預測的低延遲對於實時應用來講是非常重要的,尤其是一些關鍵性的在線商業服務以及數據庫。以消息系統爲例,在大多數的消息系統中,慢消費者會讀取backlog,這就可能會造成性能降低,其原因是這些慢的消費者會讀取辭舊存儲介質,導致I/O抖動以及內存也的換近和換出。當寫、tailing 讀以及追趕讀共享一個磁盤路徑是,就會出現上述問題。

BK中bookie設計時爲寫入,tailing read以及catch-up read使用三個獨立的I/O路徑。寫入和tailing read 要求可預測的延遲,catch-up read要求吞吐。爲這幾種workload提供物理隔離可以充分利用:

  • 網卡入帶寬和寫入時的順序寫帶寬
  • 網卡出帶寬和多塊ledger磁盤讀取的IOPS(input/output operations per second)

I/O隔離意味着BK可以提供多種優勢並且不會妨礙其他優勢。

Data distribution

構建在BK上的服務以segment ledger的形式存儲log stream,這些segment會被複制到多個bookie。這樣做帶來了一系列的優勢: 寫入的高可用、負載均衡以及簡化的操作體驗。

首先,一個log stream的容量不會被限制到單個機器的存儲容量,數據可以存儲在整個集羣上。

其次,擴展BK cluster時不需要log stream進行均衡。擴展BK只需要加入新的機器即可。新的bookie節點會被集羣發現,然後立即寫入可用。BK可以提供多種數據放置的策略,包括機架感知、區域感知以及基於權重等。

再次,有助於更快、更高效的失敗恢復。當一個segment丟失(由於機器故障)或者崩潰(由於磁盤故障),BK可以確定需要修復哪一個segment(re-replicating entries to meet the replicas requirement),然後以多對多的形式併發修復。

相比於以分區爲核心的系統(Kafka),BK的水平擴展具有優勢的,log stream(類比於kafka的分區)順序存儲在一組機器上。擴展kafka時,需要負載均衡,這是一個資源密集、易錯並且昂貴的操作。另外,partition-centric system的系統,單個機器故障或者磁盤故障都會導致複製全部的log stream。
logstream

Figure 1. Log stream: 所有的log segment都會被複制到配置數量的bookie’上(這裏爲3),並且數據可以跨一個配置的bookie上(這裏爲4). Log segment 均勻的分佈,因此均有水平擴展的能力,並且不需要均衡。

Scalability

作爲一個實時的log stream存儲,當負載增加或者愈來愈多的數據寫入時,擴展性是十分重要的。BK的擴展性主要有一些因素決定:

Number of ledgers/streams

Stream的擴展性是指BK可以支撐大量的log stream, 幾百到幾百萬個ledger,並且可以保證一致性。獲取這種特性的關鍵是數據的存儲格式。如果ledgers存儲在獨立的文件,在刷盤時,就會導致I/O分散在磁盤上。BK按照一種交替的格式存儲數據,不同的ledger或者stream的數據會匯聚,然後存儲在一個大文件中並且會做索引。這樣降低了文件數量以及I/O操作,是的BK可以支撐大量的ledger或者stream。

Number of bookies

Bookie擴展性是通過增加bookie數量來支撐快速增長的流量。BK之中,bookie之間不會有直接的交互。這就允許BK通過增加機器就可以擴展集羣,並且當超過了I/O帶寬之後,也不需要數據均衡。這就允許BK集羣在擴展時可以不用考慮數據的分佈。Yahoo和Twitter的BK單個集羣擁有10w+節點。

Number of clients

Client擴展性是指 log stream 存儲支持大量併發client和大量扇出。通過以下要點實現:

  • client和server都是Netty做異步的網絡I/O。所有的網絡I/O都是複用一個tcp鏈接下,並且都是異步的。這樣提供了一個十分高效的流水線和十分高的吞吐,並且資源消耗較少。

  • 數據會複製到多臺bookie上,多個bookie上的數據保持一致性。和Kafka等其他系統從leader 幾點讀取數據不一樣,BK client可以從任意的bookie副本上讀取數據,可以獲取高吞吐以及讀負載的分佈。

  • 因爲client可以從任意的bookie副本讀取數據,應用可以配置一個較高的副本數量來獲取更高的讀取性能。

Single stream throughput

應用程序可以通過增加stream數量或者bookie數量的方式來增加吞吐。另外,BK可以通過增加ensumble的大小來爲單個stream增加吞吐。這對於那些需要保證數據順序的有狀態應用很有用。

Operational simplicity

BK設計爲操作簡單的。可以在系統運行時通過添加bookie的方式擴展容量。如果一個bookie不可用,其上的所有entries都被標記爲under replicated,然後BK會自動從其他的可用的副本上將數據複製到新的bookie上。BK提供對於bookie的只讀模式。在一些特定的場景下,bookie自動切換到只讀模式,比如:磁盤滿,磁盤損壞等。只讀bookie不再接受寫入,但是可以提供讀服務。

另外,BK提供多種方式來管理集羣:使用管理員 CLI 工具,使用 Java admin library 或者使用 HTTP REST API。REST API 可以用於實現外部工具。

Security

BK提供了一個可插拔的鑑權機制。BK也可以配置支持多種鑑權機制。一個鑑權 provider 會創建客戶端的identity,然後爲client分配一個identifier(標識符),這個標識複用來決定client的那種操作是被授權的。BK模式提供兩種鑑權機制:TLS和SASL(Kerberos)。

發佈了14 篇原創文章 · 獲贊 0 · 訪問量 3233
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章