《設計資料密集型應用》- Designing Data-Intensive Application - 第9章 一致性與共識 讀書筆記
一致性與共識
一致性保證
分散式一致性與事務隔離的區別
- 事務隔離主要是為了避免由於同時執行事務而導致的競爭狀態
- 分散式一致性主要關於,面對延遲和故障時,如何協調副本間的狀態
線性一致性
即原子一致性(atomic consistency),強一致性
滿足標準:使系統看起來只有一個副本
-
對於多副本系統,如果第一次從A副本讀取,第二次從B副本讀取同樣的資料,有可能第二次看到的是舊資料,不滿足線性一致性
- 解決方法:為讀取新增約束,如果第一次讀取讀到新值,則後續讀取也必須保證讀到新值
線性一致與可序列化
- 可序列化為事務隔離範疇,保證每個事務在下一個事務開始之前完成
- 線性一致性是讀取和寫入暫存器(單個物件)的新鮮度保證
依賴線性一致性(線性一致性的應用場景)
-
鎖定和領導選舉
- 如在單主複製的系統中,使用分散式鎖,拿到鎖的節點判定為主節點,其鎖必須是線性一致的:所有節點對拿到鎖的鎖的節點的達成一致,如ZK、ETCD
-
約束和唯一性保證
- 在分散式資料庫中,各節點對唯一鍵的儲存,賬戶餘額等,均要求所有節點都同意一個最新的值
-
跨通道的時序依賴
-
如圖所示非同步圖片縮放服務,如果第5步取圖片時,第2步儲存未完成,則有可能拿到舊版本或是空的影象源導致錯誤
-
實現線性一致的系統
-
單節點無法滿足容災要求,考慮用複製解決問題
-
單主複製–如果讀取時會訪問到不同節點,由於延遲,有可能不會線性一致
-
共識演算法–包含防止腦裂和陳舊副本的措施,線性一致
-
多主複製–由於在多個節點上寫入,並非同步複製到其它節點,非線性一致
-
無主複製
- 嚴格的法定人數下,針對讀操作,在資料不一致的情況下可以實施同步讀修復;進行寫操作之前必須法定數量節點的最新狀態。這會引起效能損失。當一個鍵有多個併發寫入時,不能保證線性一致性。
-
線性一致性的代價
-
CAP定理
- 一致性,可用性和分割槽容錯性:三者只能擇其二。不幸的是這種說法很有誤導性【32】,因為網路分割槽是一種錯誤,所以它並不是一個選項:不管你喜不喜歡它都會發生
-
線性一致性和網路延遲
- 即使單臺計算機中,CPU上的記憶體都不是線性一致的
- Attiya和Welch 【47】證明,如果你想要線性一致性,讀寫請求的響應時間至少與網路延遲的不確定性成正比
順序保證
順序與因果
-
因果順序不是全序的
-
線性一致性
- 任意兩個操作,都能判定其先後順序
-
因果性
- 針對於具有因果關係的兩個操作,其具備順序性。線性一致性上的所有操作均有序。
-
-
線性一致性強於因果一致性
- 在所有的不會被網路延遲拖慢的一致性模型中,因果一致性是可行的最強的一致性模型
-
捕獲因果關係
- 基於資料版本
序列號順序
-
針對於單主複製,可為每個操作賦與一個單獨的遞增操作序號
-
非因果序列號生成器(非單主複製)
-
例子與存在問題
-
總共兩個節點,一個產生奇數,一個產生偶數
- 如果奇數或偶數節點產生運算元遠大於另一節點,則無法根據數字大小判斷操作因果關係
-
將物理時鐘的時間戳加到每個操作上
- 時鐘偏移問題導致因果偏差
-
預先分配序列號塊,如節點A從序列號1-1000,節點B1001-2000
- 某操作被分配在1001-2000,而與其在因果上更晚的操作被賦予在1-1000
-
-
-
蘭伯特時間戳
-
每個節點的時間戳由各自的計數器和節點ID構成,節點ID大的判定其因果在後
- 思想:每個節點和每個客戶端跟蹤迄今為止所見到的最大計數器值,並在每個請求中包含這個最大計數器值。當一個節點收到最大計數器值大於自身計數器值的請求或響應時,它立即將自己的計數器設定為這個最大值。
- 版本向量可以區分兩個操作是併發的,還是一個因果依賴另一個;而蘭伯特時間戳總是施行一個全序
-
-
光有時間戳排序還不夠
- 如多個節點同時收到帶有相同手機號唯一性約束的建立操作(非單主),需要判定最小具有時間戳的操作成功,其餘失敗。然而如果要實時向客戶端返回建立結果,則需要收集所有節點的操作,形成操作全序列。
- 此外還需要知道操作全序列何時產生,由此引出全序廣播
全序廣播
-
基本特性
-
可靠性
- 如果訊息被傳遞到一個節點,它將被傳遞到所有節點
-
有序性
- 訊息以相同的順序傳遞給每個節點
-
-
使用全序廣播
-
ZK ETCD已實現全序廣播
-
應用
- 可序列化事務
- 實現提供防護令牌的鎖服務,如在ZK中,每個獲取鎖的請求都在日誌後附加一條訊息,且每個訊息有唯一的自增序號(zxid)
-
-
使用全序廣播實現線性一致的儲存
-
使用線性一致性儲存實現全序廣播
- 如利用自增序列將自增時獲取的數值附加訊息中
分散式事務與共識
原子提交與二階段提交(2PC)
-
兩階段提交
-
請求同步阻塞問題
-
在commit階段中,如果協調者崩潰了,參與者將處於存疑狀態,對於資料庫節點,如果在prepare階段鎖定了某行資料,則有可能由於其它事務需要修改該行而被阻塞最終導致資料庫不可用
-
在commit階段如果出現了部分參與者的commit請求丟失,則有可能出現數據不一致的問題
-
在發出commit之後協調者崩潰,而接收commit的唯一參與者也崩潰,此時事務狀態不可知
-
異常處理
-
Prepare
- 協調者接收PrepareACK超時,則About
-
Commit
- 協調者接收commitACK超時,則會一直重試Commit
-
-
-
三階段提交
-
同步非阻塞,超時涉及協調者與參與者
-
將prepare階段劃分為CanCommit與PreCommit兩階段,確保在提交commit之前所有節點狀態相同
-
在收到PreCommit請求後,參與者會在本地記錄redo與undo日誌,以便About時進行回滾
-
異常處理
-
CanCommit
- 參與者沒收到CanCommit,不做任何操作
- 協調者沒有收到參與者的CanCommit的ACK或超時,執行About
-
PreCommit
- 參與者接收PreCommit超時,則About
- 協調者接收PreCommitACK超時,則About
-
DoCommit
- 參與者接收DoCommit超時,則自動Commit
- 協調者接收DoCommitACK超時,則About
-
-
實踐中的分散式事務
-
恰好一次的訊息處理
- 在異構系統中,當且僅當所有參與者均支援分散式事務時,系統才可滿足恰好一次的語義
-
XA事務
- XA是一套API,通常是一套整合在客戶端的庫,向參與者發出請求並接收參與者的迴應,同時,使用磁碟記錄啟動/終止事務的日誌
-
懷疑時持有鎖
- 當參與者在Prepare階段申請了鎖,而在事務提交/終止前,協調者發生崩潰,這會導致參與者的鎖無法釋放(或超時才能釋放)
-
從協調者故障中恢復
- 一旦出現協調者故障導致參與者鎖不釋放,唯一的解決方法是由管理員決定事務是提交還是回滾,儘管許多XA實現均實現了啟發式決策:允許參與者主動提交或回滾事務,但這會破壞分散式事務的原子性
-
分散式事務的限制
- 協調者單點故障
- 由於應用伺服器儲存了協調者的中間結果,因此應用伺服器變為非無狀態,不能直接擴充套件
- 不能檢測系統間的死鎖(因為這將需要一個標準協議來讓系統交換每個事務正在等待的鎖的資訊)
- 不能SSI協同工作(因為這需要一個跨系統定位衝突的協議)
- 系統中任何部分失效都會導致事務失敗
容錯共識
-
基本概念
-
滿足性質
-
一致同意
- 沒有兩個節點的決定不同
-
完整性
- 每個節點只決定一次
-
有效性
- 如果一個節點決定了值 v ,則 v 由某個節點所提議
-
終止
-
由所有未崩潰的節點來最終決定值
- 即容錯性,為了防止單節點獨裁崩潰導致程序掛起,共識演算法也必須基於存活的節點推進程序產生結果
-
-
-
-
共識演算法和全序廣播
- 全序廣播相當於進了多輪共識,較著名的演算法有VSR、Raft Zab Paxos,vsr raft 和zab實現了全序廣播
-
單領導者複製和共識
- 對於單主的系統下,如果主崩潰,在沒有人工干預的情況下,系統不滿足終止性,只有在人工干預的情況下才可取得進展
-
時代編號和法定人數
- 當每次主崩潰時,會選出新的一任主節點,選舉會賦與新的遞增時代編號
- 選主與提案申請均會開始一輪投票,要求參與提案投票的節點至少有一個在選主中出現(如果沒有則有腦裂的可能,參見https://arxiv.org/pdf/1608.06696.pdf)
- 2PC需要所有參與者同意,而共識協議只需要多數同意即可
-
共識的侷限性
- 投票過程是一種同步複製過程,相比非同步複製有效能損失
- 依賴於多數節點運轉
- 大多數投票演算法假定節點數量固定,新增/刪除節點困難,即使採用了動態成員擴充套件,演算法也很難理解
- 依靠超時檢測失效節點,在網路延遲變化劇烈的環境中,可能會出現頻繁選主的問題
成員與協調服務
-
ZK特性
- 線性一致性的原子操作。當有多個節點同時進行相同的操作,只有一個能成功
- 操作的全序排序。當使用鎖或租約時,每次操作均會被賦與遞增的ZXID與cversion作為令牌
- 失效檢測。依賴於心跳檢測
- 變更通知。可接收節點的加入與退出事件所產生的通知(基於客戶端寫入新的ZK的值與臨時節點失效)
-
將工作分配給節點
- 主節點失效轉移(可藉助臨時節點檢測)
- 分割槽資源分配,當有節點加入系統時,可將現有分割槽轉移至新節點
-
服務發現
-
成員服務