Design Data Intensive Applications 筆記 (複製)

複製目的:

  • 訪問的數據地理位置更接近用戶 減小訪問延時 (多數據中心)
  • 節點宕機數據仍可用 (高可用)
  • 多副本可讀,增長讀吞吐量

主從複製:

同步複製 -> 強一致性, 弱可用性 一旦某個從節點宕機則寫失敗
解決方案:一個從節點同步複製,其餘異步複製,一旦同步節點宕機提高一個異步節點爲同步節點
新強一致性複製算法: chain replication 算法

異步複製 -> 強可用性,弱一致性 安全

增長從節點:
挑戰:主節點數據仍在寫入,直接複製會致使數據不一致
方案:
複製數據時鎖主節點不容許寫 ,太粗暴
更好的方案分3步: 服務器

  1. 對主節點數據作一個一致性快照,並記錄對應的複製日誌位置
  2. 複製快照到從節點
  3. 將快照後新增的複製日誌應用到從節點上,直到追上主節點

從節點宕機恢復:
從宕機前複製日誌位置開始追趕 網絡

主節點宕機 -> FAILOVER 機制
比較複雜,通常須要3步:併發

  1. 宕機檢測 : 通常經過心跳
  2. 從新選主,一致性算法和多數派協議
  3. 請求路由到新主節點,並通知客戶端
    問題:
  4. 數據丟失,新選的主節點沒有所有原主節點的數據
  5. 腦裂:兩個節點同時認爲本身是主節點
  6. 合理的心跳超時設置 (網絡抖動可能誤報)
    手動FAILOVER優於自動?

複製日誌實現:less

  • 基於SQL語句: (MYSQL早期版本)
    問題: 特殊函數now(), random() 等帶來的不肯定性. 自增主鍵衝突. trigger, 存儲過程等的影響
  • 基於WAL日誌: (POSTGRESQL, ORACLE)
    問題:和存儲引擎強耦合, 滾動升級可能有問題
  • 邏輯複製: 自定義結構,基於修改的數據值.
    優勢:不依賴於存儲引擎, 兼容性好
  • 觸發器(TRIGGER): (DATABUS FOR ORACLE, BUCARDO)
    優勢:靈活性
    問題:OVERHEAD

複製延時的問題:
最終一致性: 以較弱的一致性換取讀的可擴展性
一致性多弱取決於複製延時和讀取策略dom

  • read-your-own-write-consistency (read-after-write consistency): 確保本身寫入的數據必定會被隨後的讀請求讀到
    實現方式:
    若是用戶只修改自身的數據並知道自身數據的ID, 自身數據從主節點讀,其餘數據從隨機節點讀
    若是用戶能夠修改許多其餘的數據,須要客戶端記錄 key -> last modify time. 在必定時間範圍內只從主節點讀, 不然從隨機節點讀. 或者客戶端把 key+last modify time 發給查詢節點, 查詢節點比較時間戳,等待遇上再返回結果或者將讀請求
    多數據中心: 路由讀請求到同一數據中心異步

  • monotonic read: 確保第二次讀結果不會比第一次舊
    實現方式:
    對同一主鍵每次讀從同一副本(可能出現數據傾斜)分佈式

  • consistent prefix read: 確保讀寫的因果順序不會錯亂 (好比問答順序)
    實現方式:
    全局的一致性寫順序
    追蹤因果關係causual dependency的算法

經過分佈式事務解決複製延時問題.ide

多主複製:

支持多點寫入
每一個LEADER對於另外一個LEADER是FOLLOWER
主要應用場景是多數據中心
每一個數據中心有一個LEADER, 數據從LEADER複製到本數據中心的FOLLOWER和另外一個數據中心的LEADER.

性能:寫請求可由LOCAL 數據中心的LEADER處理. 不用跨廣域網
數據中心容錯: 一個數據中心宕機後另外一個數據中心仍可獨立運行,數據中心恢復後再將數據複製過去
網絡容錯:跨廣域網異步複製,暫時網絡不穩定不會影響寫入

缺點:必須解決寫衝突
設計時必須考慮自增主鍵衝突. TRIGGER, 數據完整性約束等

寫衝突檢測和解決

  • 同步檢測:等數據複製到每一個節點再檢測衝突並通知用戶. 延遲大,徹底犧牲了多主複製的優點
  • 衝突避免: 保證相同的鍵寫入到同一個數據中心, 數據HASH,路由等. 數據中心若是宕機或者用戶位置改變則會出現問題
  • 每一個寫操做一個UUID,對同一數據的最後一次寫獲勝 (基於時間戳等), 可能丟失數據
  • 每一個REPLICA有一個UUID, 對同一數據寫來自高位REPLICA獲勝, 可能丟失數據
  • 保存寫衝突數據,以後自動解決或者提示用戶解決

解決衝突的時機:
寫時:一旦衝突發生,只能後臺解決沒法讓用戶干預 (Bucardo)
讀時: 保存衝突數據,在下次讀的時候自動解決或提示用戶(CouchDB)
事務中的每一個寫衝突會單獨解決,沒法保持事務邊界

自動衝突解決:
Conflict free replicated data types
Mergeable persistent data structures (GIT)
Operational transformation

多主複製拓撲:(超過兩個MASTER)
星狀
環狀
全部對全部
須要防止循環複製(寫請求記錄通過的節點編號)
星狀或者環狀複製某個節點宕機會致使不可寫
全部對全部 容易因延遲致使數據一致性問題或寫失敗

無主複製:

沒有副本概念,客戶端直接寫多個節點
多數節點響應寫請求則成功
讀請求也發送到多個節點,版本號保證讀取最新的數據
保證: 寫成功節點數(W) + 讀節點數(R) > 集羣節點數(N)
讀寫並行發往全部節點, W, R 爲響應的節點數量
若是 W + R < N 犧牲一致性換取低延時和高可用性
通常選擇 W > N/2, R > N/2,允許最多N/2 節點宕機
W + R > N 依然有可能讀到舊數據:

  • Sloppy Quorum
  • 併發寫衝突
  • 讀寫衝突, 讀的節點可能沒寫入最新數據
  • 寫某些節點失敗,寫成功節點未回滾
  • 寫成功節點宕機,數據從舊節點恢復

監控數據陳舊程度很困難, 沒法像主從複製同樣監控REPLICATION LAG. 寫順序不肯定

Sloppy quorum: 部分節點宕機致使沒法寫入一般寫入的節點(讀寫遷移)
Hinted handoff: 節點恢復後讀寫遷移的節點將臨時寫入數據寫回一般寫入節點
Sloppy quorum 提升了可用性可是下降了讀到最新數據的可能
無主複製也適合多數據中心的場景: 寫請求發送多數據中心但只等待本數據中心確認

保證宕機節點最終一致性:
Read repair: 客戶端發現讀版本衝突自動更新版本比較舊的節點
Anti-entropy process: 後臺進程自動比較數據版本並修復 (不一致時間長)

寫衝突可能發生在如下狀況:

  • 節點因網絡故障丟失寫請求
  • 不一樣節點收到寫順序不一致

寫衝突處理:

  • 最後寫獲勝:爲寫請求分配版本號,衝突只保留版本最大的數據. 可能丟失數據,適用於每一個KEY寫一次再也不更新的場景 (時序數據?)
  • 併發寫定義: 若是兩次寫操做有強時序關係或互相依賴 (如A爲插入key1,B爲key + 1),則A,B稱爲causually dependent. 任意寫操做A, B, 必有A在B以前,A在B以後,或者AB爲併發
  • 併發寫檢測算法:
    每一個KEY維護一個版本,每次寫版本+1
    客戶端寫KEY以前必須先從SERVER讀一次KEY的最新版本
    寫操做帶上寫以前讀到的KEY版本,能夠覆蓋同一個KEY更低或者相同版本,可是更高版本必須保留 (併發寫)
    購物車舉例:
    A, B 兩個客戶端同時向購物車添加產品,每次寫提交,服務器端生成一個購物車新版本,並將最新版本的購物車內容及相應版本號返回給提交寫請求的客戶端
    客戶端提交寫請求時要將提交的內容和以前的購物車版本合併,並附帶以前的版本
    服務端針對同一個客戶端,把新的版本覆蓋以前的版本,可是不一樣客戶端提交的最新版本認爲是concurrent write,不會互相覆蓋,須要衝突解決
  • 處理併發寫:
    由客戶端合併多個concurrent write的版本.
    刪除操做不能直接物理刪除,而要標記(tombstone)
    自動衝突解決, 見上文
  • 向量鍾 (version vector): 適用於leaderless replication, 沒有主節點而是多個對等的replica版本號針對於每一個replica的每一個KEY寫以前先從replica讀取最新的version vector, 合併寫數據再發回保證從replica A讀,再寫到replica B是安全的
相關文章
相關標籤/搜索