Anna總結篇
前言
Anna是一個分散式鍵值對儲存資料庫,也就是kv store。它是一個實驗室的產物,所以設計理念上比較新穎,不過實用程度可能稍顯不足。Anna到目前為止有兩個版本,版本v0對應的是 Anna: A KVS For Any Scale 這篇論文,相應的程式碼在 ofollow,noindex">ucbrise/anna 。版本v1(曾被作者稱為bedrock)對應的論文是 Eliminating Boundaries in Cloud Storage with Anna ,程式碼在 fluent-project/fluent 。
Anna的兩個版本各自側重點不同。
Anna v0 是設計實現一個高度可擴充套件性(支援從幾臺server線性擴充套件到幾千臺server),高可用(支援資料存在多個備份),高吞吐量(採用無鎖的Actor model)的kv store。並且用一套統一的程式設計正規化(借鑑了CRDT和Bloom programming language)來實現不同等級的一致性(單key的最高到causal consistency,跨多個key的最高可以到read committed)。
Anna v1(bedrock)則是想解決目前雲端儲存中資料無法靈活擴充套件和移動的問題。比如系統同時使用了雲硬碟(如S3)和雲記憶體(如ElastiCache),因為ElastiCache價格貴,那麼如果能在負載低的時候自動把資料移動到S3上就可以減少使用費用。還有對於雲記憶體的資料熱點問題,要做到自動給熱點資料(hotspot)增加副本,從而提高整體吞吐量。Anna v1 想在效能和費用之間達到平衡,讓使用者只需要設定雲端儲存的預期費用或者預期效能指標,從而把分散式儲存系統當成黑盒。
Anna v0
Anna v0 支援的操作有:GET,PUT和DELETE。這些操作都只要和叢集中的任意一臺server完成通訊即可,即不需要quorum。其中GET可能會獲取到過期的資料,而DELETE基於PUT <key, null> 實現,即把某個key的值設定為null。
來看看Anna v0 的架構

User發起GET/PUT/DELETE請求給Client Proxy,Client Proxy根據一致性hash找到存有資料副本的多個Actor(這個和Dynamo的做法基本一樣,Dynamo的論文把這些講的很清楚了,推薦去看看),然後隨機選擇一個Actor傳送請求。注意Anna計算一致性hash的位置時,是以Actor為單位而不是單個server為單位的。單個server可以包含多個Actor,對於每一個CPU執行緒都有一個Actor與其對應。之後,收到請求的Actor再去訪問它自己的私有Actor HashTable,對裡面的資料進行操作。
這裡面有幾個問題,也基本是分散式儲存的核心問題,來看看Anna v0 是怎麼操作的。
- 不同資料副本之間如何保持同步?
首先,Anna是share nothing結構,Actor之間的通訊完全靠message queue,不共享任何狀態,也就沒有任何鎖結構。
Anna允許GET操作讀取到過期資料,所以讀操作不需要同步機制。來看PUT操作。
一個Actor收到PUT操作以後,將資料寫入本地名為changeset的結構中,並返回給客戶端成功訊號。每隔一段時間,所有Actor會將自己changeset中的資料通過message queue廣播給其他Actor,最終所有Actor都能接收到最新的資料。這個過程是非同步的,所以不會影響Actor對外繼續提供服務。上圖中的紅色箭頭(Network Multicast)就代表此過程。
在提供的程式碼中,message queue用的具體實現來自於ZeroMQ。對在同一臺server內Actor之間的通訊,和跨server的Actor之間的通訊,ZeroMQ都分別作了優化。
這種同步過程也叫gossip(很形象),就像流言蜚語的傳播一樣,一傳十,十傳百。
個人覺得這種機制的可靠性保障比較弱,有可能某些Actor會永遠得不到同步,或者同步的非常慢。當然,可以想象,這種機制的效能會比較好。
值得一提的是,Anna要求其所傳輸的資料符合ACI(Associativity, Commutativity和Idempotence)特性。參見 分散式系統一致性學習筆記(一):從操作購物車說起 。資料如果不符合這種特性,那麼理論上是需要使用者自己加上分散式鎖來自行同步資料的。 所以我認為這套機制的前景就在於符合ACI特性的資料,其表述能力可以有多強,能夠用其實現的業務邏輯的範圍有多廣。
2. 如何做到容災(fault-tolerance)?
由於資料存在多個副本,所以單個server掛掉不丟失資料。Client proxy那邊有message buffer,一定程度上能緩解單個actor在收到PUT之後,沒來得及向其他actor同步就掛掉的尷尬。
Anna v0效能
論文最後的效能測試是和Masstree, TBB, Redis, Redis Cluster來對比,不用說,當然是Anna各種完爆對手。值得說的是在把Anna的replication factor設定為full的時候,就是每個actor都儲存一份整個系統的完整資料。如果request不是嚴重傾斜在某些資料上,那麼這時Anna的效能比較差。主要效能開銷就在gossip上。。。死於八卦
Anna v1 (bedrock)
想象一下你對儲存系統的要求是什麼?是不是要在訪問速度,容量,價格之間做取捨。我們知道雲端儲存是可以按使用情況收費的,那麼好用的雲端儲存系統就應該是個黑盒。使用者只要設定好效能指標,它就會用最小的花費來達到這個目標;同樣,使用者指定預算開銷的同時,它要能達到最大的效能。
Anna v1的目標就是做成這樣一個雲端儲存系統,使用者使用時只需要設定業務指標。
遇到的挑戰有三個:
- 儲存系統的容量不夠 。這個被現有的雲端儲存解決的比較好,雖然還是有問題,比如掛載新的儲存節點太慢,可能要花費幾分鐘。
- 資料傾斜 。使用者訪問的資料集中在某幾臺server上,沒有被現有的雲端儲存很好的解決。
- 資料熱點變更 。使用者訪問的熱點資料會隨時間變化,需要動態增加熱點資料的副本,讓熱點資料重新均勻分佈於整個叢集。
Anna v1 的架構較之Anna v0 的架構變化很大。Anna v1 由monitoring system/policy engine,routing service(這個和Anna v0的client proxy比較像),cluster management system和storage kernel組成。
monitoring system/policy engine是為了監控叢集狀態,動態調整資料分佈用的,和storage kernel一起算是核心系統。
cluster management system是負責執行policy engine的指令,進行資源的新增釋放等操作。
Storage Kernel
在Anna v0的基礎上增加了不少內容。增加了tier的概念。
不同tier對應不同的儲存結構,比如由記憶體構成的雲記憶體是tier 0,由硬碟構成的雲硬碟是tier 1等等。
一個storage node包含多個worker thread,每個worker thread有自己的私有資料結構儲存資料,worker thread之間通過message queue通訊,不共享任何記憶體資料結構。
這基本沿用了Anna v0的設計。

Global hash ring用來確定資料在哪一層的哪個storage node上,local hash ring被storage node用來確定資料存在於自己的哪個worker thread上。
其實global hash ring就相當於Anna v0的hash ring。新增一個local hash ring是因為worker thread的數量在一個storage node裡是能夠動態改變的,而Anna v0的一個server所能包含的actor是由CPU有幾個執行緒決定的,是固定值。
monitoring system/policy engine

policy engine做的就是獲取叢集的使用情況:storage consumption, compute consumption和key access frequency,然後根據使用者的配置和預定的規則作出圖中右上角的三種操作其中一種。論文作者也提到,這部分或許可以用機器學習的方法替代基於規則的方法。
元資料
Anna v1 儲存了一個replication vector的元資料來控制所有的資料分佈。每個key有一個replication vector。
長這樣 [< R1,......Rn >;< T1,......Tn >]
Ri表示tier i 中儲存這個key的node的數量,Ti表示tier i 中每個node使用了多少個worker thread來儲存這個key。通過控制每個key的replication vector,就能控制這個key在每個tier的儲存情況。
比如預設有兩個tier,tier 0是可擴充套件的雲記憶體層,tier 1是雲硬碟。
每個key預設的replication vector是[<1, k>, <1, 1>],k是使用者設定的replication factor。表示在記憶體層上有一個storage node儲存了這個key;在硬碟層上有k個storage node儲存了。並且這些storage node都只使用了一個worker thread來儲存這個key(node內部沒有再啟用副本機制)。
如果要讓資料從記憶體層移動到硬碟層(省錢),只要改變把這個key的replication vector改成[<0, k+1>,<1, 1>]就行。同理可以讓熱點資料分佈到更多的node上解決熱點問題。
Anna v1 效能
論文使用了不同的資料來測試,顯示Anna v1在資料請求突然加大、資料熱點變更的時候都能比較好的保持住效能,不超過設定的lantency值。同時,在同樣的預算花費情況下,Anna v1也比Anna v0有更高的吞吐量。
總結
Anna的設計有很多值得借鑑,或者至少給人以啟發的地方。論文提到的問題也確實是當前設計kv store和使用雲端儲存的一些痛點,期待伯克利的RISE Lab持續更新。