天天看點

網易視訊雲:分布式一緻性

網易視訊雲是網易傾力打造的一款基于雲計算的分布式多媒體處理叢集和專業音視訊技術,提供穩定流暢、低延遲時間、高并發的視訊直播、錄制、存儲、轉碼及點播等音視訊的PAAS服務,線上教育、遠端醫療、娛樂秀場、線上金融等各行業及企業使用者隻需經過簡單的開發即可打造線上音視訊平台。現在,網易視訊雲的技術專家給大家分享一則技術文:分布式一緻性。

分布式系統的一緻性問題總是伴随資料複制而生, 資料複制技術在提高分布式系統的可用性、可靠性和性能的同時,卻帶來了不一緻問題。 理想情況下, 多個副本應該是應用透明的, 從外界看來多副本如同單副本, 而事實上維護一緻性非常困難。試想一下, 寫入新資料時, 某副本所在的伺服器當機,或者突然發生了網絡錯誤, 此時該如何處理? 是繼續重試等待故障消失呢,還是放棄寫入故障副本? 若繼續重試寫入, 則導緻系統不可用,影響業務; 而放棄寫入則副本資料不同步,産生了差異, 會不會對應用會造成影響? 這似乎是個兩難問題, 看似無法抉擇。 幸好一緻性問題并不是非黑即白的二選一問題, 業界早就定義了多套适合于各種應用場景的一緻性模型[1,2]:

假設有一個存儲系統, 它底層是一個複雜的高可用、高可靠的分布式存儲系統。一緻性模型定義如下:

1. 強一緻。 按照某一順序串行執行存儲對象讀寫操作, 更新存儲對象之後, 後續通路總是讀到最新值。 假如程序A先更新了存儲對象,存儲系統保證後續A,B,C的讀取操作都将傳回最新值。

2. 弱一緻性。 更新存儲對象之後, 後續通路可能讀不到最新值。假如程序A先更新了存儲對象,存儲系統不能保證後續A,B,C的讀取操作能讀取到最新值。 從更新成功這一刻開始算起, 到所有通路者都能讀到修改後對象為止, 這段時間稱為”不一緻性視窗”, 視窗内通路存儲時無法保證一緻性。

3. 最終一緻性。 最終一緻性是弱一緻性的特例, 存儲系統保證所有通路将最終讀到對象最新值。 譬如, 程序A寫一個存儲對象, 如果對象上後續沒有更新操作, 那麼最終A,B,C的讀取操作都會讀取到A寫入的值。 “不一緻性視窗”的大小依賴于互動延遲,系統的負載,以及副本個數等。

最終一緻性是最常見的一種弱一緻性, 從單個用戶端(使用者)角度出發, 最終一緻性可再細分為以下幾種:

1 Causal consistency. A更新了對象之後通知B,那麼B能讀取A寫入的值, 而與A沒有因果關系的C則可以讀不到最新值。 以微網誌為例, 使用者甲釋出微網誌之後, 系統通知了它的粉絲, 那麼粉絲必須能看到甲發表的微網誌。

2 Read your wirte consistency. 用戶端總是能讀取到自己的寫入最新值。 以部落格為例, 使用者必須能看到自己剛發表的部落格。

3 Session consistency. 用戶端與伺服器維護一個會話, 會話有效期間, 滿足read your write consistency。

4 monotonic read consistency. 如果用戶端已經讀取了對象, 那麼後續讀取操作不能讀到該對象的舊值。 以郵件收件箱為例, 使用者跑到上海的時候必須也能讀到他在杭州已經讀過的郵件。

5 monotonic write consistency. 單用戶端發出的寫操作串行執行。

設計系統時, 該如何選擇一緻性模型? 一般來說, 首選強一緻性, 原因是了解起來簡單, 使用起來也簡單。 但CAP理論[3]告訴我們一緻性、可用性和網絡分區三者不可兼得, 是以弱一緻性也有其适用場景: (1)網絡不穩定的情況,譬如跨資料中心服務, 廣域網服務(譬如DNS,CDN等); (2) 可用性要求非常高,并發更新非常少, 且沖突容易解決的場景, 譬如amazon購物車; (3) 一緻性要求本來就不高的場景, 譬如緩存。 如果确實有必要選擇弱一緻性,那麼盡量選擇最終一緻性, 并從使用者角度角度出發選擇一種合理的最終一緻性。

強一緻實作方法

強一緻性模型有幾種常見實作方法, 主從同步複制, 以及quorum複制等。

Quorum複制[10]

假設N是副本數, 寫Quorum W是需要寫操作成功之前必須更新的副本數目 , 寫Quorum R是讀操作傳回之前必須讀取的副本數目。 當W + R > N時, 讀寫操作必然有交集, 讀操作必能讀到最新資料, 是以Quorum複制能做到強一緻。

然而, 何為“最新”資料, 往往挺難界定, 是以實際應用中, 一般給資料帶一個版本号(或者時間), 用于辨別資料的新舊。 僅憑資料版本号仍不足以保證正确性, quorum複制還得處理失敗寫操作帶來的版本覆寫問題。 按照定義, 更新副本數目小于W的寫操作算失敗, 由于失敗操作的版本可能大于最後一次有效寫入操作的版本, 失敗的殘留可能會遮蓋最新有效版本, 導緻讀Quorum達不到R, 也就是最新有效版本丢失! 為了解決版本覆寫問題, 支援quorum的存儲系統一般需要儲存曆史版本, 讀資料時一次性讀多個版本, 剔除那些失敗的寫入資料,得到正确的資料。

Quorum複制的好處靈活選擇W、R, 可用性比較好, 響應時間比較低(不必等待慢節點傳回結果), 缺點是維護資料曆史版本代價較高,

吞吐率也不高, 常見的應用場景是中繼資料存儲。

主從同步複制

主副本或者全局協調者負責所有讀寫操作, 同時負責串行化寫操作, 将寫操作實時同步傳播到其他副本, 副本全部寫成功之後, 寫操作才傳回給使用者。 采用主從同步複制的典型例子是 Ceph[4], InnoSQL[11] FSR。 其優點是實作簡單, 寫吞吐率較高, 也能容忍更多節點故障。 缺點有三個,一是可用性較差, 檢測到主故障并重新選主需要一定時間, 一般是10s以上; 二是響應時間較Quorum方法長, 必須等待所有副本都響應了, 請求才算成功; 三是一般隻能從主節點讀取資料,讀吞吐率較差。

主從強一緻複制的典型方法如下:

主從鍊式複制[5]。 副本按照一定順序關系組織成複制鍊, 寫操作必須發送到主副本, 串行化之後依次傳播到其他副本, 讀操發往最後一個副本。 原始的鍊式複制雖然能保證強一緻,但是讀取的吞吐率較差, CRAQ[6]針對這點進行優化, 通過維護一個髒标記CRAQ允許讀取任意副本, 進而提高了讀吞吐率。

PacificA複制[8]。 PacificA是微軟提出的一種保證強一緻的主從複制方法, 适用于基于日志的存儲系統, 分布式日志系統kafka就是采用這種做法。 PacificA複制分為三個階段。 第一階段prepare, 主節點确定更新順序, 發送資料到所有從節點, 從節點寫入日志并傳回響應給主節點。 主節點收到所有從節點的響應之後進入第二階段“主commit”, 主節點在本地commit該資料, 緊接着傳回用戶端, 此後用戶端能讀到該資料。 第三階段從commit, 主節點本地commit後,異步(或者通過後續prepare消息捎帶)commit所有從節點。 該協定保證更新串行性, 確定資料一但commit就能被讀到。

複制狀态機

狀态機複制[9]。 利用分布式一緻性(consensus)協定,譬如Paxos/Raft[12], 建構一個分布式複制的強一緻日志, 日志中記錄副本上的讀寫操作, 每個副本從相同的初始狀态出發, 執行相同的日志序列, 就得到一緻的結果。 當然每個副本各執行日志, 是以日志進度不完全同步。 一般是選擇一個副本作為master, 用戶端的讀寫操作都發往master, master執行完寫操作,并寫入日志之後就傳回給用戶端。

弱一緻實作方法

弱一緻性實作方法可分為兩類

單副本更新, 異步擴散到其他副本。 好處是沒有更新沖突, 缺點是可用性和性能不足。

多副本支援更新, 好處是可用性和性能較好, 但要解決更新沖突問題。 更新沖突一般通過last write wins是解決, 由于分布式系統不存在絕對時間, last write win的難點是無法界定”最新的寫操作“。 弱一緻性系統無法為資料維護單調遞增的資料版本号, 是以工程實作上一般使用vector clock[7]檢測資料沖突。 如果vector clock還無法解決沖突問題,那麼必須引入應用語義, 以購物車為例, 發生沖突時, 隻要将兩個版本的購物車的内容合并起來, 雖然會出現一些已經删除的商品, 但對使用者體驗影響不太大。

繼續閱讀