Dynamo: Amazon’s Highly Available Key-value Store 讀書筆記

第一節 介紹
   介紹Amazon的面臨的情況:在高峯期面臨百萬級客戶的併發訪問,所以可靠性尤爲重要,任何的小的"斷檔期",都會帶來財務上的損失和影響客戶的信任。隨着業務量的增長,平臺也要有更高的可伸縮性(scalable)。

第二節 背景
   Amazon有數以百計的服務需要暴露,後面支持的服務器數以萬計部署在世界各地的數據中心。這些服務分有狀態的(依賴其他服務)和有狀態的(狀態要持久化)。傳統的系統設計把狀態(state)保存到關係數據庫。但關係數據庫的複製需要昂貴的硬件費用和高技能的人操作,效率不高。另外可靠的複製技術的限制、重視一致性超過可靠性。
   Dynamo:高可靠的數據存儲技術。標記服務分類、簡單的key-value接口、清晰的一致性窗口定義、用簡單的方案記錄數據增長量和服務請求率。每個服務有自己的Dynamo實例。


  系統預期和需求
    • QueryModel:簡單。Amazon的服務查詢使用key,不需要用關聯Schema來完成。存儲的對象較小<1M.
    • ACID:ACID (Atomicity, Consistency,Isolation,Durability),包保證ACID,勢必降低可靠性。Dynamo的目標是輕一致性而重可靠性。不提供任何Isolation保證而允許簡單的根據關鍵字key更新操作。
    • Efficiency:性能、效率成本、可靠性、持久性的折中。
    • Other Assumptions:Dynamo是內部系統,沒有安全的要求,例如認證和授權。

  服務協議SLA(Service Level Agreements)
  • 參照取平均值,但不侷限於此,致力於最高的可用率。
  設計的考慮點
  • 什麼時候執行解決更新衝突的過程?這個需求要求將複雜的衝突在read解決,保證write不會被拒絕(即使出現一些網絡異常等),“總是可寫”。
  • 誰來執行衝突解決方案?一種是在數據存儲層,只能用簡單的策略,如:採用“以最後更新爲準”;一種是在應用層來維護衝突解決方案。
  • 其他一些關鍵的設計原則:
    • Incremental scalability:增加一個新的node,應該對已有的系統和操作影響最小。
    • Symmetry:每個節點都是同等的。
    • Decentralization:分散化,簡單而可伸縮。
    • Heterogeneity:支持異構化,不因增加一個node而需要一次性更新所有已有的節點。

第三節 相關工作
   點對點系統
      介紹了一些知名的系統。

   分佈式的文件系統和數據庫
  • Dynamo的Distributed blockstorage系統採用將大對象分割成小block來存儲,原因(a)爲了存儲小對象< 1M);(b)key-value 存儲方式易於配置。
  • Dynamo目標:使僅要求key/value訪問的應用主要關注可用性:不拒絕寫,即使在網絡分區出現問題或系統故障時。
      
   討論
  • Dynamo 目標在於系統“總是可寫”
  • Dynamo 是在一個簡單的可管理的domain裏,所有的節點都是可信任的。
  • 使用Dynamo的應用不要求分等級的命名空間。
  • Dynamo 是爲latency敏感的應用而建立的,它要求至少99.9%的幾百毫秒的read和write操作響應。
  • 所以避免路由請求要通過多個節點。因爲多個節點multihop路由給響應時間增加可變性,而且增加很高百分比的latency。
  •  Dynamo 形容爲一個zero-hop DHT,每個節點本地維護足夠的路由信息,直接路由請求到精確的節點。

第四節 系統架構
     Dynamo使用的技術和優勢


       系統接口
              get(),put()簡單接口。

       分區算法



      Dynamo 採用一段連續變化的hash值(簡單的使用[10, 20]的一個),而不是採用一個節點映射到環裏的一個點,任何一個節點都會複製給其他多個節點。Dynamo採用“虛擬節點”的概念。一個虛擬節點像系統的一個節點,但是每個節點都可以由一個以上的虛擬節點的負責(獲得響應)。當一個新的節點被加入系統,它就被賦予了環中的多個位置(“token”)。使用虛擬節點的優點:
  • 如果一個節點不可用(由於故障或例行維護),原來由這個節點完成的負載就分發到剩餘可用的節點上。
  • 當一個節點又恢復可用,或者一個新的節點加入系統,新可用節點可以從其他節點接收相當數量的負載。
  • 虛擬節點的數量是由它自己的容量決定的,根據物理基礎架構的不同而不同。

      複製
      爲了達到高可用和高持久性的目的,Dynamo複製它的數據到多個hosts,每個數據項被複制到N個hosts,N是一個“每個實例”的參數配置。每個key k被賦予一個協調者節點,協調者掌管數據項的複製,保證屬於它(NODE)的範圍。此外,本地保存每個key在它(協調者NODE)的範圍內,協調者還複製這些key到N-1個環中順時針方向的節點。具體見上圖,節點B複製key k到節點C和D,並保存在本地。節點D將保存key將落在(A, B], (B, C], and (C,D]。
      優先列表:負責保存特殊key的節點列表,至少包括N個節點,小於等於N個物理節點,所以優先列表忽略環中的一些位置來保證列表中包含不同的物理節點。

      數據版本
      爲了保證寫的可用性,Dynamo 把每次修改看成一個新的不便的數據版本(dataversion),它允許對象的多個數據版本同時出現在系統內。大多數情況下,新版本包含之前的版本,系統自己可以決定有效的版本(根據語意協調syntacticreconciliation)。但是可能出現版本分支,系統無法協對象調多版本衝突,那客戶必須執行調解保證多分枝數據版本合併成一個。
       Dynamo採用向量時鐘vector clocks 來捕同一個對象不多版本的因果關係。一個 vectorclock是有效的(節點,計數器)的列表. 一個向量時鐘vectorclock關聯了所有對象的所有版本。通過檢查向量時鐘可以決定哪兩個版本是並行分支或有一個因果順序。 One candetermine
whether two versions of an object are on parallel branches orhave
a causal ordering, by examine their vector clocks.如果計數器在第一個對象的時鐘小於等於第二個時鐘所有節點的計數器,它就是第二個時鐘的父親,可以被忘記,否則,這2個變化被認爲是衝突需要協調。


       一個可能的問題:向量時鐘長度由於多server同等的對同一個對象寫而不斷增加?在實踐中,這個不可能,因爲寫通常被優先列表中的TOPN個節點處理,只有在網絡分區或多服務器不可用的情況下,纔會有優先列表中非TOPN的節點處理,這樣纔會使向量時鐘增長。Dynamo最後採用下面截斷方式處理clock truncationscheme:在每個(節點,計數器) 中, Dynamo保存一個時間戳表示更新數據項的最後時間。當(節點,計數器)的對的數量超過一定的閥值例如10,最老的對就會被從時鐘移除。顯而易見,這種截斷的方式可能會導致在協調衝突是變得無效,例如後代關係不夠精確。但是這個問題沒有在生產環境出現,而且這個問題還沒有調查研究。
      
     執行get()和put()操作
     這節爲了簡化,僅描述讀寫操作在無異常的環境的情況。根據Amazon的基礎架構規範,通過http訪問get和put操作。客戶端選擇節點有2種策略:(1)路由客戶端請求通過總的負載均衡器基於負載信息來選擇一個節點。好處:客戶端不用將Dynamo硬編碼在應用裏。(2)使用分區敏感的客戶端庫/包請求直接到達適當的協調者節點。好處:能達到較低的延遲(latency),因爲它跳過潛在的forward步驟。
爲了維護複製的一致性,Dynamo採用一個一致性協議類似配額制quorum systems:這個協議有2個關鍵的配置項: R 和 W。R是必須參與成功讀操作的最少節點數;W是必須參與成功寫操作的最少節點數。根據配額制quorum-like system設置R和W滿足R+ W >N(N爲優先列表中的前N個節點)。在這個模型中,讀寫操作最差的延遲由R或W中最慢的複製決定。由於這個原因,所以讀和寫通常配置小於N來達到較好的延遲betterlatency。
     一旦協調者收到put()請求,它將產生一個新版本的向量時鐘,在本地寫入新的版本,並將新版本(連同新向量時鐘)發送給N個最好級別的可達節點。如果有至少W-1個節點響應寫操作就認爲是成功了。
     類似,對應get()請求,協調者請求所有已存在的數據版本,針對key從優先列表中的N個最高級別的可達節點獲取,在返回給客戶端之前,要等待R個響應。如果協調者整理收集多個數據版本,它返回所有它認爲這些沒有關聯的版本。有分歧的版本被協調,協調後的版本(取代當前版本的)被回寫。
       
       失敗處理:提示移交HintedHandoff
       如果Dynamo採用傳統的配額制,在服務器宕機或網絡分區異常,它將降低持久性(一個已提交事務的任何結果都必須是永久性的,即“在任何崩潰的情況的能保存下來”。),即使是最簡單失敗情況也是如此。
爲了完善這個問題,採用“寬鬆的配額制”,所有的讀寫在優先列表不一定總是前N個健康的節點。
      參考分區算法的圖,N=3,這個例子,如果在寫操作的過程中節點A臨時宕機或不可達,複製將發送到節點D,這個是爲了保證期望的可用性和持久性。發送達節點D的複製包括一個提示在它的元數據中:建議哪個節點是期望的接收節點(這個例子是A)。接收到暗示的節點將保存寫的數據在獨立的本地數據庫,並定期掃描。
一旦偵測到節點A恢復,節點D將嘗試發送的複製信息到節點A,一旦傳輸成功,節點D將刪除本地的這些臨時對象而不損失複製信息。
      爲了提升應用的高可用,可以將W設置爲1,它表示寫操作只要一個節點寫到本地存儲就被認爲成功,這樣只有所有的節點在不可用是寫操作才被拒絕。但是,實際上,大多數Amazon應用在生產環境設置W爲一個比較大的值,主要是爲了滿足期望的持久性程度。

       處理永久性失敗情況:同步複製
       如果系統成員變化較低,提示移交方案較好。有一些場景暗示:在這些場景下,在返回源複製節點前提示移交方案不可用。Dynamo實現反熵(同步複製)協議保持複製同步。
     爲了偵測快速複製和最小化傳輸數據之間的矛盾,Dynamo採用Merkle trees方案。Merkletree是一個Hash樹,葉節點是每個key的hash值,父節點的值是它子節點的值的hash(key的hash的hash)。好處:Merkletree的每個分支可以獨立檢查,而不需要整棵樹所有數據。而且在複製檢查不一致性時,Merkletree可以幫組減少所需傳遞的數據。例如兩棵樹的root節點的hash值相同,表示樹的葉節點都相同,所以不需要同步。如果不同,兩個node之間要叫號樹上子節點的hash只,直到樹的葉節點。主機host可以識別出不需要同步的keys。Merkletree最小化同步複製傳輸的數據,減少磁盤讀操作的執行。
       每個節點維護各自獨立的Merkletree,在一定的key範圍內。這使得節點可比較key是否在範圍內。這個方案中,2個節點交換對應的Merkletree,那麼它們對應的key範圍是共有的。隨後,採用樹反轉方案(treetraversal)描述上述節點,決定它們是否有一些不同而需要執行同步動作。這個方案缺點:但一個節點加入或退出系統,大量key範圍變化要求tree重新計算。
     
       成員資格和失敗偵測
       .環成員資格
      由於Amazon環境內的節點的不可用通常是透明的,但可能持續較長時間。一個節點很少明確表示永久的離開,而且不應該導致重新均衡分區的分配,或重新修復不可達的複製。所以採用顯式的機制初始化加入或去除環中的節點。自適應成員關係管理協議(gossip-basedprotocol)傳遞成員資格變化和維護最終的成員的一致性視圖。每個節點每秒隨機選擇一個對等節點,這兩個節點有效地協調它們之間持久的成員資格變更歷史信息。
       當一個節點首次開始,它選擇自己token集合(在一段連續hash空間的多個虛擬節點),映射節點和token值。這些映射信息將持久化到本地磁盤,最初只包含本地節點和token集合信息。保存在Dynamo不同節點的映射信息在同一個通訊交換時候協調成員資格變更歷史信息。而且分區信息和部署信息經一起gossip-basedprotocol傳遞,每個存儲節點都知道它的對等節點能處理的token範圍。這使得每個節點直接forward一個key的讀/寫操作到正確的節點集合。

       .外部發現
       爲了避免邏輯隔離,一些Dynamo節點要作爲種子節點的角色。種子節點經由外部機制發現,所有節點都知道。因爲所有幾點最後都和一個種子節點協調成員資格,邏輯隔離不太可能做到。種子節點可用從配置文件或配置服務中獲取。

       .失敗偵測
      失敗偵測爲了避免在get和put操作,或傳輸分區信息和提示覆制時,嘗試與不可達的節點通訊。節點A人爲節點B失敗,如果節點B不能響應A的消息(即使節點B響應節點C的消息)。如果客戶端請求在一定頻率,產生Dynamo環中的內部節點通訊,節點A很快發現節點B沒有響應。節點A使用備選的節點服務請求,即與B分區映射的節點。節點A定期嘗試檢查節點B是否恢復。如果沒有客戶請求的驅動兩個節點之間的通訊,沒有任何一個節點知道對方是否可達或有響應。
       去中心化失敗偵測協議使用簡單的gossip-style協議,使得系統中的每個節點了解其他節點加入或推出。前期的設計Dynamo使用了一個去中心化的失敗偵測機制維護一個全局一致的失敗狀態視圖。後期決定採用
節點明確的加入或退出方法,避免採用全局失敗狀態視圖。因爲所有節點可以通過顯式地調用加入和推出方法來得到通知持久節點的加入和退出通知;而臨時節點的失敗,通過各自節點與其他節點的通訊異常來偵測。

       .增加和刪除存儲節點
      當一個新的節點X加入到系統,它將獲得多個Token並隨機散佈到環裏。對於每個賦予節點X的key範圍,
有一些節點(<=N)目前是這些節點負責的,節點X的key範圍中的key原則是落在這些節點的key範圍內的。
爲了分配key範圍給節點X,一些已經存在的節點不再擁有一些key,而是交給節點X。
       這種方案可以平均的分配存儲節點的負載,對應滿足延遲需求和保證快速加載比較重要。最後,需要在源和目的節點之間增加一個確認的回合,確認目的節點沒有收到2次重複的key範圍。

       
第五節 實現細節

  
在Dynamo,每個存儲節點有3個軟件組件:請求協調組件,成員資格和失敗偵測組件,本地持久化引擎組件。所有組件均採用Java。
   Dynamo的本地持久化組件支持不同存儲引擎插件。引擎使用Berkely DB傳統數據存儲,BDBjava版本,MySQL和擁有持久化支持的內存緩衝。
可以插入式的持久化組件設計,主要原因是爲了針對應用訪問模式採用適當的存儲引擎。例如,BDB能處理的對象主要是kb級別的,MySQL能處理更大的對象。應用選擇Dynamo的本地持久化引擎取決於它們對象大小的分佈。絕大多數Dynamo的生產實例主要使用BDB傳統數據存儲。
   請求協調組件建立在事件驅動消息方式,底層消息處理管道被劃分成多個步驟,類似SEDA架構。所有通訊均採用JavaNIO通道。每個客戶端請求導致節點上創建一個狀態機服務請求。狀態機包含所有業務邏輯:識別可以響應某個key的節點,發送請求,等待響應,嘗試重發,處理恢復和打包響應給客戶端。每個狀態機實例處理對應的一個客戶端請求。
   在讀操作的響應返回到調用者後,狀態機等待一個短暫的時間去接收任何重要的響應。如果在一些返回的響應中有舊的版本信息,協調這會用最新版本去更新這些節點。這個過程稱爲“讀修復”,因爲它修復這些節點的複製信息,這些節點在某個時間剛好缺失最近的一個更新,也能減輕對反熵協議(同步)的依賴。
   在先前的章節,寫請求被優先列表中的前N個節點之一協調處理。儘管它總是期望TopN的第一個節點協調寫操作並在一個地點序列化所有的操作,但是這種方案會導致不均衡的分發負載,從而破壞SLA協議。這是因爲請求沒有根據對象做負載均衡。爲了解決這個問題,優先列表中的任意一個節點都可以協調寫操作。特別地,既然每個寫操作通常緊跟在讀操作之後,協調者會選擇先前響應讀操作最快的節點來處理寫操作,這個信息是保存在請求的上下文信息中的。這個優化可以選擇擁有這個數據的節點,而且可以提升獲得"readyourwrites"的一致性。另外它也可以減少請求處理性能的變化,99.9%可以提升性能。
  
 
第六節經驗分享
    .業務邏輯詳細描述協調邏輯:這是Dynamo的典型場景。每個數據對象被複制到多個節點。在出現歧義版本的情況下,客戶應用執行它自己的協調邏輯。
    .基於時間戳的協調邏輯:和上面的模式不同在於協調機制。Dynamo執行簡單的基於時間戳的協調邏輯-“最近修改爲準”。
    .高性能的讀引擎:當Dynamo要建成一個“總是可寫”的數據存儲,一些服務優化自己的配額特徵(quorum),使用它作爲一個高性能的讀引擎。典型地,這些服務器擁有一個較高的讀請求率和僅僅一些少量的更新。在這種配置下,通常R被設置爲1,W爲N。對於這些服務,Dynamo提供分區和複製數據到多個節點,而且提供可增加的伸縮性(Incrementalscalability)。這些實例功能中一些針對數據的權威持久化緩存,將數據存儲在一些重量級的存儲上。維護產品分類和推銷產品的服務適合這種方案。

   
Dynamo的主要優點是客戶端應用可以優化參數N,R,W來獲取它們期望的性能,可用性和持久性的級別。
例如N的值決定每個對象的持久性。Dynamo的用戶通常使用N=3.
   W和R的值影響對象的可用性,持久性和一致性。例如,如果W設爲1,那麼只要還有至少一個節點在系統中,
系統將不會拒絕寫請求。但是,W和R的值過低會增加不一致性的風險,由於寫請求被人爲成功而返回給客戶端,
甚至沒有處理大多數的複製。這也表明有一個易被攻擊的窗口:當寫請求成功地返回給客戶端時,正是它只在一
小部分節點做了持久化。通過增加W的值可以增加降低被攻擊的風險,但是可能增加拒絕請求的可能性(而且降低可用性),因爲更多的存儲節點需要處理一個寫請求。
   通常在Dynamo的幾個實例配置(N,R,W)爲(3,2,2)。這些值滿足性能,持久性和可用性的SLA協議。

    平衡性能和持久性
    一個典型的DynamoSLA需要要求:99.9%的讀/寫請求在300ms內執行完。
  
    圖4
   寫操作的延遲總是高於讀操作,顯然是因爲寫操作總是導致磁盤訪問。99.9%延遲在200ms附近,密度要高於平均值。這是因爲99.9%延遲受以下幾個因素影響:請求負載,對象大小,地點位置策略。
   當一些面向客戶的服務要求更高的可用級別,Dynamo提供用持久性換取性能保證的方案。優化每個存儲節點:在主內存中維護一個對象緩衝(objectbuffer)。每個寫操作保存在緩衝中,使用一個寫線程定期獲取寫操作,將對象保存到存儲中。在這種方案中,讀操作首先檢查請求的key是否在緩衝中,如果有,存儲引擎直接從緩衝讀取對象返回。
  
    圖5
   這種優化後延遲是峯值的1/5,甚至對應一個保存1000個對象的小緩衝也是這樣。顯然這個方案是用持久性換取性能,在這種情況下,一臺Server宕機導致緩衝隊列中的寫操作丟失。爲了減少持久性風險,寫操作改進:讓協調者選擇N個節點中的一個進行“持久化寫操作”。既然協調者只等待W個響應,所以在一個簡單的複製中,寫操作的性能不受持久化寫操作的性能影響。
  
   保證負責分佈的均衡
   
Dynamo使用一致性hash算法區分key空間,覆蓋對應對象的複製,保證負載分佈均衡。一個均衡的key分佈有助於獲取均衡的複製分佈。實際上,Dynamo的設計預測即使在有嚴重傾斜的訪問部分,也是有大量的熱門的key分佈於此,以至於能通過分區均衡地將熱門key負載傳播到其他節點。
   一個節點人爲均衡,它的負載偏移在15%內,否則人爲不均衡。不均衡節點的百分比稱爲不均衡率。
   
    圖6
   針對以上實例,低負載的不均衡率高達20%,高負載接近10%。直觀感覺,在高負載下,大量熱門key被訪問,由於key的均衡分佈,所以負載也均衡。但是在低負載下(1/8峯值),很少熱門key被訪問,導致很高的不均衡率。
   
   Dynamo的分區方案:
   策略1:每個節點T個隨機Token和基於Token值進行分區:這是最早部署在生產環境的策略(在4.2節中描述)。在這個方案中,每個節點被分配T個Tokens(從哈希空間隨機均勻地選擇)。所有節點的token,是按照其在哈希空間中的值進行排序的。每兩個連續的Token定義一個範圍。最後的Token與最開始的Token構成一區域(range):從哈希空間中最大值繞(wrap)到最低值。由於Token是隨機選擇,範圍大小是可變的。節點加入和離開系統導致Token集合的改變,最終導致key範圍的變化,請注意,每個節點所需的用來維護系統的成員的空間與系統中節點的數目成線性關係。
   在使用這一策略時,遇到了以下問題。首先,當一個新的節點加入系統時,它需要“竊取”(steal)其他節點的鍵範圍。然而,這些需要移交keyranges給新節點的節點必須掃描他們的本地持久化存儲來得到適當的數據項。請注意,在生產節點上執行這樣的掃描操作是非常複雜,因爲掃描是資源高度密集的操作,他們需要在後臺執行,而不至於影響客戶的性能。這就要求我們必須將引導工作設置爲最低的優先級。然而,這將大大減緩了引導過程,在繁忙的購物季節,當節點每天處理數百萬的請求時,引導過程可能需要幾乎一天才能完成。第二,當一個節點加入/離開系統,由許多節點處理的keyrange的變化以及新的範圍的MertkleTree需要重新計算,在生產系統上,這不是一個簡單的操作。最後,由於keyrange的隨機性,沒有一個簡單的辦法爲整個key space做一個快照,這使得歸檔過程複雜化。在這個方案中,歸檔整個keyspace 需要分別檢索每個節點的key,這是非常低效的。
   這個策略的根本問題是,數據劃分和數據安置的計劃交織在一起。例如,在某些情況下,最好是添加更多的節點到系統,以應對處理請求負載的增加。但是,在這種情況下,添加節點(導致數據安置)不可能不影響數據劃分。理想的情況下,最好使用獨立劃分和安置計劃。爲此,對以下策略進行了評估:

   策略2:每個節點T個隨機token和同等大小的分區:在此策略中,節點的哈希空間分爲Q個同樣大小的分區/範圍,每個節點被分配T個隨機Token。Q是通常設置使得Q>>N和Q>>S*T,其中S爲系統的節點個數。在這一策略中,Token只是用來構造一個映射函數該函數將哈希空間的值映射到一個有序列的節點列表,而不決定分區。分區是放置在從分區的末尾開始沿着一致性hash環順時針移動遇到的前N個獨立的節點上。圖7說明了這一策略當N=3時的情況。在這個例子中,節點A,B,C是從分區的末尾開始沿着一致性hash環順時針移動遇到的包含keyK1的節點。這一策略的主要優點是:(i)劃分和分區佈局解耦(ii)使得在運行時改變安置方案成爲可能。
     
  圖7

  
   策略3:每個節點Q/S個Token,大小相等的分區:類似策略2,這一策略空間劃分成同樣大小爲Q的散列分區,以及分區佈局(placementof partition)與劃分方法(partitioningscheme)脫鉤。此外,每個節點被分配Q/S個Token其中S是系統的節點數。當一個節點離開系統,爲使這些屬性被保留,它的Token隨機分發到其他節點。同樣,當一個節點加入系統,新節點將通過一種可以保留這種屬性的方式從系統的其他節點“偷”Token。

   對這三個策略的效率評估使用S=30和N=3配置的系統。然而,以一個比較公平的方式這些不同的策略是很難的,因爲不同的策略有不同的配置來調整他們的效率。例如,策略1取決於負荷的適當分配(即T),而策略3信賴於分區的個數(即Q)。一個公平的比較方式是在所有策略中使用相同數量的空間來維持他們的成員信息時,通過評估負荷分佈的偏斜.例如,策略1每個節點需要維護所有環內的Token位置,策略3每個節點需要維護分配到每個節點的分區信息。

   在我們的下一個實驗,通過改變相關的參數(T 和Q),對這些策略進行了評價。每個策略的負載均衡的效率是根據每個節點需要維持的成員信息的大小的不同來測量,負載平衡效率是指每個節點服務的平均請求數與最忙(hottest)的節點服務的最大請求數之比。
 
   
    圖8
   
正如圖中看到,策略3達到最佳的負載平衡效率,而策略2最差負載均衡的效率。一個短暫的時期,在將Dynamo實例從策略1到策略3的遷移過程中,策略2曾作爲一個臨時配置。相對於策略1,策略3達到更好的效率並且在每個節點需要維持的信息的大小規模降低了三個數量級。雖然存儲不是一個主要問題,但節點間週期地Gossip成員信息,因此最好是儘可能保持這些信息緊湊。除了這個,策略3有利於且易於部署,理由如下:(i)更快的bootstrapping/恢復:由於分區範圍是固定的,它們可以被保存在單獨的文件,這意味着一個分區可以通過簡單地轉移文件並作爲一個單位重新安置(避免隨機訪問需要定位具體項目)。這簡化了引導和恢復過程。(ii)易於檔案:對數據集定期歸檔是Amazon存儲服務提出的強制性要求。Dynamo在策略3下歸檔整個數據集很簡單,因爲分區的文件可以被分別歸檔。相反,在策略1,Token是隨機選取的,歸檔存儲的數據需要分別檢索各個節點的key,這通常是低效和緩慢的。策略3的缺點是,爲維護分配所需的屬性改變節點成員時需要協調。

    有分歧的版本:何時有?有多少?
   如前所述,Dynamo被設計成爲用一致性換取可用性。爲了解不同的一致性失敗導致的確切影響,多方面的詳細的數據是必需的:中斷時長,失效類型,組件可靠性,負載量等。詳細地呈現所有這些數字超出本文的範圍。不過,本節討論了一個很好的簡要的度量尺度:在現場生產環境中的應用所出現的不同版本的數量。
   有分歧的版本的數據項出現在兩種情況下。首先是當系統正面臨着如節點失效故障的情況下,數據中心的故障和網絡分裂。二是當系統的併發處理大量寫單個數據項,並且最終多個節點同時協調更新操作。無論從易用性和效率的角度來看,都應首先確保在任何特定時間內不同版本的數量儘可能少。如果版本不能單獨通過矢量時鐘在語法上加以協調,他們必須被傳遞到業務邏輯層進行語義協調。語義協調給服務應用引入了額外的負擔,因此應儘量減少它的需要。
   在我們的下一個實驗中,返回到購物車服務的版本數量是基於24小時爲週期來剖析的。在此期間,99.94%的請求恰好看到了1個版本。0.00057%的請求看到2個版本,0.00047%的請求看到3個版本和0.00009%的請求看到4個版本。這表明,不同版本創建的很少。
   經驗表明,有分歧的版本的數量的增加不是由於失敗而是由於併發寫操作的數量增加造成的。數量遞增的併發寫操作通常是由忙碌的機器人(busyrobot-自動化的客戶端程序)導致而很少是人爲觸發。由於敏感性,這個問題還沒有詳細討論。
    
   客戶端驅動協調 或 服務器端驅動協調
   
Dynamo有一個請求協調組件,它使用一個狀態機來處理請求。客戶端的請求均勻分配到環上的節點是由負載平衡器完成的。Dynamo的任何節點都可以充當一個讀請求協調者。另一方面,寫請求將由key的首選列表中的節點來協調。此限制是由於這一事實--這些首選節點具有附加的責任:即創建一個新的版本標識,使之與寫請求更新的版本建立因果關係。請注意,如果Dynamo的版本方案是建基於物理時間戳的話,任何節點都可以協調一個寫請求。

   另一種請求協調的方法是將狀態機移到客戶端節點。在這個方案中,客戶端應用程序使用一個庫在本地執行請求協調。客戶端定期隨機選取一個節點,並下載其當前的
Dynamo成員狀態視圖。利用這些信息,客戶端可以從首選列表中爲給定的key選定相應的節點集。讀請求可以在客戶端節點進行協調,從而避免了額外一跳的網絡開銷,比如,如果請求是由負載平衡器分配到一個隨機的Dynamo節點,這種情況會招致這樣的額外一跳。如果Dynamo使用基於時間戳的版本機制,寫要麼被轉發到在key的首選列表中的節點,也可以在本地協調。
   一個客戶端驅動的協調方式的重要優勢是不再需要一個負載平衡器來均勻分佈客戶的負載。公平的負載分佈隱含地由近乎平均的分配
key到存儲節點的方式來保證的。顯然,這個方案的有效性是信賴於客戶端的成員信息的新鮮度的。目前客戶每10秒隨機地輪循一Dynamo節點來更新成員信息。一個基於抽取(pull)而不是推送(push)的方被採用,因爲前一種方法在客戶端數量比較大的情況下擴展性好些,並且服務端只需要維護一小部分關於客戶端的狀態信息。然而,在最壞的情況下,客戶端可能持有長達10秒的陳舊的成員信息。如果客戶端檢測其成員列表是陳舊的(例如,當一些成員是無法訪問)情況下,它會立即刷新其成員信息。

表二:客戶驅動和服務器驅動的協調方法的性能。

 

99.9th百分讀延時(毫秒)

99.9th百分寫入延時(毫秒)

平均讀取延時時間(毫秒)

平均寫入延時(毫秒)

服務器驅動

68.9

68.5

3.9

4.02

客戶驅動

30.4

30.4

1.55

1.9


   表顯示了24小時內觀察到的,對比於使用服務端協調方法,使用客戶端驅動的協調方法,在99.9百分位延時和平均延時的改善。如表所示,客戶端驅動的協調方法,99.9百分位減少至少30毫秒的延時,以及降低了34毫秒的平均延時。延時的改善是因爲客戶端驅動的方法消除了負載平衡器額外的開銷以及網絡一跳,這在請求被分配到一個隨機節點時將導致的開銷。如表所示,平均延時往往要明顯比99.9百分位延時低。這是因爲Dynamo的存儲引擎緩存和寫緩衝器具有良好的命中率。此外,由於負載平衡器和網絡引入額外的對響應時間的可變性,在響應時間方面,99.9th百分位這這種情況下(即使用負載平衡器)響應時間比平均情況下要高。

   
平衡後臺任務 vs 前臺任務   
   
每個節點除了正常的前臺
put/get操作,還將執行不同的後臺任務,如數據的副本的同步和數據移交(由於提示或添加/刪除節點導致)。在早期的生產設置中,這些後臺任務觸發了資源爭用問題,影響了正常的putget操作的性能。因此,有必要確保後臺任務只有在不會顯著影響正常的關鍵操作時運行。爲了達到這個目的,所有後臺任務都整合了管理控制機制。每個後臺任務都使用此控制器,以預留所有後臺任務共享的時間片資源(如數據庫)。採用一個基於對前臺任務進行監控的反饋機制來控制用於後臺任務的時間片數。
   管理控制器在進行前臺
put/get操作時不斷監測資源訪問的行爲,監測數據包括對磁盤操作延時,由於鎖爭用導致的失敗的數據庫訪問和交易超時,以及請求隊列等待時間。此信息是用於檢查在特定的後沿時間窗口延時(或失敗)的百分位是否接近所期望的閥值。例如,背景控制器檢查,看看數據庫的99百分位的讀延時(在最後60秒內)與預設的閾值(比如50毫秒)的接近程度。該控制器採用這種比較來評估前臺業務的資源可用性。隨後,它決定多少時間片可以提供給後臺任務,從而利用反饋環來限制背景活動的侵擾。

   討論

   
本節總結了在實現和維護
Dynamo過程中獲得的一些經驗。很多Amazon的內部服務在過去二年中已經使用了Dynamo,它給應用提供了很高級別的可用性。特別是,應用程序的99.9995%的請求都收到成功的響應(無超時),到目前爲止,無數據丟失事件發生。
    此外,
Dynamo的主要優點是,它提供了使用三個參數的(NRW),根據自己的需要來調整它們的實例。不同於流行的商業數據存儲,Dynamo將數據一致性與協調的邏輯問題暴露給開發者。開始,人們可能會認爲應用程序邏輯會變得更加複雜。然而,從歷史上看,Amazon平臺都爲高可用性而構建,且許多應用內置了處理不同的失效模式和可能出現的不一致性。因此,移植這些應用程序到使用Dynamo是一個相對簡單的任務。對於那些希望使用Dynamo的應用,需要開發的初始階段做一些分析,以選擇正確的衝突的協調機制以適當地滿足業務情況。最後,Dynamo採用全成員(fullmembership)模式,其中每個節點都知道其對等節點承載的數據。要做到這一點,每個節點都需要積極地與系統中的其他節點Gossip完整的路由表。這種模式在一個包含數百個節點的系統中運作良好,然而,擴展這樣的設計以運行成千上萬節點並不容易,因爲維持路由表的開銷將隨着系統的大小的增加而增加。克服這種限制可能需要通過對Dynamo引入分層擴展。此外,請注意這個問題正在積極由O(1)DHT的系統解決

第七節結論
   本文介紹了Dynamo,一個高度可用和可擴展的數據存儲系統,被Amazon.com電子商務平臺用來存儲許多核心服務的狀態。Dynamo已經提供了所需的可用性和性能水平,並已成功處理服務器故障,數據中心故障和網絡分裂。Dynamo是增量擴展,並允許服務的擁有者根據請求負載按比例增加或減少。Dynamo讓服務的所有者通過調整參數N,R和W來達到他們渴求的性能,耐用性和一致性的SLA。
   在過去的一年生產系統使用Dynamo表明,分散技術可以結合起來提供一個單一的高可用性系統。其成功應用在最具挑戰性的應用環境之一中表明,最終一致性的存儲系統可以是一個高度可用的應用程序的構建塊。

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章