1. 最終一致性
1.1. 在一些應用領域,通常談論的是銀行和金融行業,最終一致性根本不合適
1.2. 事實上,最終一致性在銀行業已經使用了很多年
-
1.2.1. 支票需要幾天時間才能在你的賬戶上進行核對,而且你可以輕鬆地開出比賬戶餘額多的支票
-
1.2.2. 當處理檢查並建立一致性後,你才能看到一些後果
1.3. 在過去的好日子裡,系統的所有資料項都有一個單一的真實來源,即資料庫,不存在副本一致性的問題,因為根本沒有副本
1.4. 為了保證每個節點的資料高可用,還需要對每個節點的內容進行復制,以消除單點故障
-
1.4.1. 當資料庫節點和網路快速且可靠地工作時,使用者不知道他們正在與分散式系統互動
-
1.4.2. 副本似乎是即時被更新的,同時使用者請求的處理響應時間很短
-
1.4.2.1. 不一致的讀取很少見
-
1.4.3. 出現故障意味著你的資料庫副本保持不一致的時間可能比你的應用程式能容忍的時間更長
1.5. 不一致視窗
-
1.5.1. 最終一致系統中的不一致視窗是資料物件被更新並傳播到所有副本所需的持續時間
-
1.5.2. 在領導者-追隨者模式的系統中,領導者協調其他副本進行更新
-
1.5.3. 在無領導系統中,任何副本(或者是任何資料庫節點——這取決於實現)都會協調更新
-
1.5.4. 當所有副本都具有相同的值時,不一致視窗結束
-
1.5.5. 影響不一致視窗持續時間的因素
-
1.5.5.1. 副本數目
> 1.5.5.1.1. 副本越多,需要協調的副本更新就越多
> 1.5.5.1.2. 只有當所有副本都相同時,不一致視窗才會關閉
> 1.5.5.1.3. 擁有的副本越多,不一致視窗受個別響應緩慢的副本影響而導致視窗延長的可能性就會增加
- 1.5.5.2. 運維環境
> 1.5.5.2.1. 任何瞬時操作故障,例如瞬時網路故障或資料包丟失,都會延長不一致視窗
> 1.5.5.2.2. 副本存在更新延遲的主要原因可能是節點上的大量讀/寫工作負載
> 1.5.5.2.2.1. 導致副本過載併產生額外的資料傳播延遲
> 1.5.5.2.3. 資料庫承受的負載越多,不一致視窗可能就越長
- 1.5.5.3. 副本之間的距離
> 1.5.5.3.1. 如果所有副本都在同一個區域網子網上,通訊延遲可能是亞毫秒級的
> 1.5.5.3.2. 只要有一個副本跨越大陸或世界各地,不一致視窗的最小值都將是副本之間的往返時間
- 1.5.5.4. 所有這些問題都意味著你無法控制不一致視窗的持續時間
1.6. 讀寫一致性
-
1.6.1. 讀寫一致性是系統的一個屬性,它確保如果客戶端對資料進行持久更改,更新後的資料值由同一客戶端的任何後續讀取返回
-
1.6.2. 不一致視窗期間客戶端有可能的情況
-
1.6.2.1. 釋出對資料庫資料物件鍵的更新
-
1.6.2.2. 對相同的資料庫物件鍵發出後續讀取操作,由於它訪問的是一個副本,未保留最近的更新,導致訪問到過時的資料
-
1.6.3. 採用領導者-追隨者副本時,實現讀寫一致性很簡單
-
1.6.3.1. 對於要求讀寫一致性的用例,你只需確保後續讀取由領導者副本處理
> 1.6.3.1.1. 可以保證讀取的是最新的資料物件值
-
1.6.4. MongoDB的預設行為是透過訪問主副本實現
-
1.6.5. 在Neo4j叢集中,所有寫入操作都由領導者處理,領導者非同步更新只讀的副本
-
1.6.5.1. 讀取操作可能由副本處理
1.7. 最終一致資料庫廣泛應用於大型系統中
-
1.7.1. 線上博彩和遊戲行業依賴於高可用性和低延遲
-
1.7.2. 寫入Riak KV的資料自動寫入跨全球分散式叢集的多個副本,具有可調節一致性,以便使用者訪問靠近其物理位置的副本來提供高可用性和低延遲
1.8. 最終一致資料庫已經成為可擴充套件分散式系統領域的一個既定部分
-
1.8.1. 簡單的、可演化的資料模型透過自然分割槽和複製來實現可擴充套件性和可用性,為許多網際網路規模的系統提供了出色的解決方案
-
1.8.2. 最終一致資料庫難免為系統提供過時的資料
-
1.8.3. 大多數資料庫都提供可調節一致性,允許系統設計人員平衡讀取和寫入的延遲,並權衡可用性和一致性以滿足應用程式需求
-
1.8.4. 對資料庫中不同副本的同一物件的併發寫入會導致衝突
2. 可調節一致性
2.1. 許多最終一致的資料庫允許你透過配置選項和API引數來定製資料庫的最終一致行為,可以根據用例能容忍的副本最終一致性級別來權衡讀寫操作的效能
2.2. 可調節一致性基於要完成資料庫請求必須訪問的特定副本數
3. 讀取和寫入仲裁
3.1. 法定數仲裁是多數副本,即(N/2)+1
3.2. 對於三個副本,法定數仲裁意味著寫入必須在兩個副本上成功,而讀取必須訪問兩個副本
3.3. 仲裁的直觀表現是,始終在大多數副本中讀取和寫入,讀取請求將看到資料庫物件最新版本的值
3.4. 如果法定數的節點不可用,寫入和讀取將失敗
3.5. 寬鬆仲裁(sloppy quorum)
-
3.5.1. 寬鬆仲裁第一次出現在Amazon的早期Dynamo論文的描述中,並在DynamoDB、Cassandra、Riak和Voldemort多個資料庫中實現
-
3.5.2. 如果副本節點不可用而導致寫入操作無法達到給定的法定數,則將更新臨時儲存在另一個可訪問的節點上
4. 副本修復
4.1. 在分散式、自我複製資料庫中,你希望每個副本都是一致的
4.2. 系統會隨著時間的推移趨向於熵(無序)
- 4.2.1. 資料庫需要採取積極措施來確保副本保持一致,這些措施統稱為反熵修復
4.3. 主動修復
-
4.3.1. 在訪問物件時應用程式的主動修復
-
4.3.2. 主動修復對於頻繁讀取的資料庫物件有效
-
4.3.3. 主動副本修復也稱為讀取修復,是在響應資料庫讀取請求時發生的
-
4.3.4. 如果有任何值不一致,協調器將向副本發回最新值以更新過時的值
-
4.3.5. 讀取修復的工作方式取決於資料庫實現
4.4. 被動修復
-
4.4.1. 對於不常訪問的物件(很可能是你的絕大多數資料),使用被動修復策略
-
4.4.2. 被動反熵修復通常是一個定期執行的程序,旨在修復不常訪問的副本
-
4.4.3. 構建Merkle樹是一種CPU和記憶體密集型操作,該操作要麼按需啟動(由管理工具啟動),要麼定期安排
5. 衝突處理
5.1. 在無領導系統中,寫入操作可以由任何副本處理
5.2. 最後寫入者勝出
-
5.2.1. 決定最終值的一種方法是使用時間戳
-
5.2.2. 為更新請求生成一個時間戳,資料庫確保併發寫入發生時,具有最新時間戳的更新成為最終版本
-
5.2.3. 機器上的時鐘會漂移
-
5.2.3.1. 更新請求由兩個或多個獨立程序在不同副本上的同一資料物件執行
-
5.2.3.2. 這些更新必須被視為同時的或併發的
-
5.2.3.3. 附加到更新請求的時間戳只是施加了任意順序來解決衝突罷了
-
5.2.4. 使用最後寫入者勝出策略來解決衝突,資料丟失是不可避免的
-
5.2.5. 在資料庫中安全地使用純粹的最後寫入者勝出策略的唯一方法是確保所有寫入都使用唯一鍵儲存資料物件,並且物件在後續操作中是不可變的
-
5.2.5.1. 對資料庫中資料的任何更改都需要讀取現有資料物件,並使用新鍵將新內容寫入資料庫
5.3. 版本向量
-
5.3.1. 每個唯一的資料庫物件都與版本號一起儲存
-
5.3.2. 如果寫入的版本號與資料庫物件版本不匹配,則發生衝突,資料庫必須採取補救措施以確保資料不丟失
-
5.3.3. 管理版本向量是資料庫的責任
-
5.3.3.1. 資料庫客戶端只需要提供帶有更新的最新版本,並在衝突發生時進行處理
-
5.3.4. 邏輯時鐘
-
5.3.4.1. CPU測量的物理時間在分散式系統中不是可靠的時間來源
-
5.3.4.2. Leslie Lamport在他的開創性論文中首次提到了邏輯時鐘
> 5.3.4.2.1. 這項工作的本質是happens-before關係的定義
> 5.3.4.2.2. 如果一個程序發生操作a(例如,一個資料庫請求),並在它完成後發生操作b,則a happens-before b。這由a→b表示
> 5.3.4.2.3. 如果一個程序向另一個程序傳送訊息m,則傳送發生在接收之前,即傳送happens-before接收
> 5.3.4.2.4. 如果兩個獨立的程序執行操作a→b和c→d,則無法定義{a,b}和{c,d}之間的順序
> 5.3.4.2.5. 關係happens-before是可傳遞的,如果a→b和b→c,則a→c
- 5.3.4.3. 系統可以使用邏輯時鐘捕獲happens-before關係,使用簡單的計數器和演算法達成
> 5.3.4.3.1. 每個程序都有一個本地時鐘,程序啟動時將其初始化為零
- 5.3.4.4. Lamport時鐘定義了部分事件之間的順序,然而它無法辨別沒有因果關係的併發請求
> 5.3.4.4.1. 它不能用於檢測資料庫衝突
> 5.3.4.4.1.1. 這便是版本向量的使用場景了
-
5.3.5. Redis、Cosmos DB和Riak等資料庫正在利用研究社群的最新成果來支援名為CRDT(無衝突資料副本型別)的資料型別集合
-
5.3.5.1. CRDT的一個簡單示例是一個可用於維護社交媒體網站上使用者的關注者數量的計數器