如何理解高可用數據複製原理
一談到複製技術, 相信我們大部分都有一個認知, 那就是實現數據存儲的高可用, 其實進行數據複製也不僅僅是實現高可用, 同時也是邊緣加速以及提升讀性能的一個技術手段, 今天我就來講述下複製技術原理, 也是作爲學習過程中的一個筆記記錄.
共享存儲與無共享存儲架構
同樣我們關注一項技術還是需要先了解過往的背景, 現在我們可以先思考下爲什麼數據庫數據需要分佈在多臺機器上呢?
首先我們先來看單機的共享內存架構, 所有處理器都能以大致相同的性能訪問相同的 RAM 和磁盤, 這種架構相當標準, 如下所示:
在上述架構中如果我們要擴展到更高的負載, 最簡單的方式就是購買更高的機器配置, 將多個 CPU、內存芯片以及磁盤塊在一個操作系統下組合起來, 快速的互連組件允許任意 CPU 訪問內存或磁盤的任何部分。在這種共享內存架構中,所有組件都可視爲一臺單一機器.
共享內存方法的問題在於成本增長速度超過線性比例:一臺擁有兩倍 CPU、兩倍內存和兩倍磁盤容量的機器,其成本通常遠高於兩臺普通機器的成本之和。而且由於瓶頸的存在,規模兩倍的機器未必能處理兩倍的負載。即成本與擴展的侷限性.
其次是有限的故障容忍能力, 即使具備熱插撥組件也存在單點問題, 換而言之如果節點發生宕機就全部不可用. 即有限的故障容忍能力, 不具備高可用.
上述我們講到是共享內存架構, 那如果是共享磁盤的方法呢? 對於這類方法我們會有一個很熟悉的應用場景, 那就是數倉, 可以通過彼此相互獨立的 CPU 以及內存將數據通過互聯網的方式連接共享磁盤, 即:
看到這裏我們可能會想共享磁盤的方法相比共享內存的方式在擴展性以及故障容忍性更好, 其一是我們通過加相同的 CPU 以及 RAM 機器來提升計算性能, 其二是每臺機器彼此獨立, 相比共享內存架構, 如果機器發生故障, 其他機器仍然能夠訪問磁盤數據.
但共享磁盤架構也存在單點問題, 即如果數據在到達存儲子系統之前發生硬件或者軟件故障導致數據被損壞, 即使採用 RAID 或者其他冗餘技術, 被損壞的數據也將會被冗餘存儲, 那麼這個時候所有機器節點訪問的數據的時候都受到影響. 同樣是有限故障容忍, 存在單點問題.
除此之外, 如果數據需要在多臺機器進行交互協調, 由於數據是共享的, 那麼必然存在鎖爭用問題, 競爭和鎖定開銷會限制共享磁盤方法的可擴展性。
基於擴展性、高可用以及成本問題, 我們需要將數據分佈在多臺機器上, 每個機器都彼此有自己獨立的 CPU、內存以及磁盤數據, 彼此之間互不共享, 彼此通信通過網絡互聯組成, 即:
無共享架構能夠在擴展性、高可用以及成本等相比共享存儲層面有一定的優勢, 這個時候我們的數據由單臺變成了多臺分佈, 此時我們再來回答數據爲啥需要分佈在多臺機器上, 主要原因有:
這也是我們進行復制的原因, 何爲複製? 複製就是通過網絡連接的多臺機器保留相同的數據副本, 通過數據複製一是可以讓我們的系統在離用戶更近的地理位置上加速訪問減少延遲; 二是即使部分節點發生故障也能保證系統穩定運行; 三是通過數據副本冗餘可以將數據讀取分散到其他機器, 提升數據查詢的擴展能力.
數據複製面臨的問題
在一個數據無共享架構中, 如果我們要進行數據複製, 那麼我們會面臨以下幾個問題:
-
對於數據量級的擴展, 我們一般採用的手段就是分片技術, 採用分片主要從兩個層次考慮, 其一是數據容量, 比如用數據容量有 100gb, 而我們單臺節點不足以存儲 100gb, 那麼就擴展一臺機器來分擔; 其次是規模, 比如 1000w 份數據, 每份數據存儲可能 1byte, 可能也就是 10gb, 其實單節點是足以存儲, 這個時候擴展一臺機器更多是想將數據分散, 分擔流量提升性能的目的. 因此我們的數據複製是建立在每臺機器能夠容納整個數據集的副本基礎上.
-
數據複製最大的難點就是要捕獲數據變更並複製到對應的數據副本節點上, 一般有三種複製方式, 即 single-leader(單領導者)、multi-leader(多領導者 / 數據分片 / 多數據中心) 以及 leaderless(無領導者) 複製. 那麼如何保證數據副本的一致性?
-
數據複製是通過網絡進行通信的, 也就是變成發送數據複製節點與接收數據節點之間的通信, 在計算機層面節點 / 程序之間通信要麼是同步要麼異步, 因此數據複製應當採用哪種複製方式呢? 同時由於網絡不可靠導致複製延遲如何保證數據副本一致性?
-
如果數據副本失效, 即不可用, 該採取什麼樣的策略與措施呢? 數據如何追趕到與其他數據副本保持一致呢?
看到上述的問題都有一個共性, 那就是數據一致性問題, 我覺得我們都不陌生了, 既然是數據一致性問題, 這個時候我們可以採用之前的一個框架來輔助我們進行決策:
數據複製原理
在存儲高可用架構中, 數據複製其實就是實現數據冗餘的落地方式, 而我們要進行數據複製就需要有對應的複製格式以及複製方式. 即:
數據複製格式
- 基於命令 / 語句的複製
在增量複製的場景下, 也許我們可以採用上述的複製格式, 因爲實現簡單, 複製數據量小, 但是這種方式存在數據安全問題. 爲什麼呢? 比如像上述的 SQL, 使用函數 NOW() 來獲取時間和日期將會和 Node1 節點產生不一致; 其二是執行命令不確定是否存在前置數據依賴, 即無法保證語句執行的順序性, 那就有可能產生數據結果不一致. 這種的不一致是安全性問題, 需要我們手動修復解決的, 那麼如何避免呢? 那就是使用確定性的值替換對應的不確定性命令 / 函數操作. 這個時候我們就有了基於數據複製的格式.
- 基於數據複製
同樣適用於增量複製場景, 實現簡單, 能夠保證數據安全性, 但存在複製流量很大. 爲什麼流量會很大? 比如上述 SQL 語句, 原本我是複製一條 SQL 語句即可, 現在我是直接複製數據值, 那麼一條 SQL 改動影響了多少行那麼我就得複製多少行過去. 但是基於數據複製有時候考慮的一些特殊情況, 比如遷移上雲可能需要數據脫敏處理, 一般我們更多采用基於日誌 / 文件的格式進行復制.
- 基於日誌的複製格式
基於文件的複製格式在實現上比較複雜, 同樣能夠保證數據一致性, 同時複製的時候數據是隨着時間變化着的, 複製流量也會很大.
基於日誌的複製方式一般有兩種, 一是存儲引擎的 WAL 方式, 不論是 LSM 還是 B 樹結構, 都是追加字節序列, 能夠包含對數據庫的所有寫入操作, 因此可以同步到另一個副本上進行數據重放構建; 然而它的不足就是與存儲引擎強相關, 依賴於存儲引擎執行恢復, 因爲 WAL 日誌包含具體哪些磁盤塊對應的哪些字節被更改的信息, 使得複製與存儲引擎緊密耦合. 試想下如果 Node1 節點的存儲引擎版本升級, Node2 並沒有升級的話, 中間複製協議的版本就可能存在差異.
另外一種基於日誌複製的方式是基於邏輯日誌複製, 如果是 mysql 的話那麼基於邏輯日誌是一個二進制日誌, 也可以理解爲基於行復制, 主要是解決與存儲引擎解耦, 這種更具備向後兼容性. 即使是不同的存儲引擎版本也不影響到複製協議, 甚至是不同存儲引擎也能夠實現日誌複製, 比如搜索引擎需要依賴 mysql 同步數據, 那麼就可以基於邏輯日誌複製到 ES 中寫入倒排索引提供搜索召回查詢.
- 基於應用程序 / 代碼複製
一般採用這種複製方式可能是要做異構數據存儲, 需要彙總不同數據來源進行加工處理重新存儲到新的存儲介質以滿足對應的需求. 當然在數據庫層面也存在基於應用程序的複製, 比如觸發器和存儲過程.
數據複製方式
- 同步複製
在上述同步複製架構中, 我們能夠實現最強一致性, 即讀己之所寫, 也就是客戶端寫入一個數據值, 再次讀取就能夠讀取到最新值. 但是由於 Node1 節點需要等待 Node2 以及 Node3 節點的數據同步, 如果當 Node2 以及 Node3 節點發生不可用的時候, Node1 將無法提供客戶端寫入操作, 因此故障容忍度比較低. 即我們所說的可用性低.
- 異步複製
相比同步複製, 我們的 Node1 接受客戶端寫入數據並持久化到本地, 同時向 Node2 以及 Node3 發起異步數據複製方式, 這個時候 Node1 不需要等待 Node2 以及 Node3 的回覆, 這個時候寫入性能高, 即使 Node2 或者 Node3 節點發生不可用也能夠繼續對外提供服務, 因此故障容忍度相比同步複製高.
但並不是沒有代價, 客戶端再次讀取數據有可能是讀取到舊值, 其次由於異步複製, Node1 節點是不知道其他節點是否與自己存儲的數據是一致的, 如果 Node1 節點不可用需要從 Node2 以及 Node3 中進行選舉, 如果我們選舉 Node2 作爲頂替 Node1 節點, 但是 Node2 節點此時並未完全同步到 Node1 節點已存儲的數據, 那麼就會出現我們所謂數據丟失現象. 數據丟失與我們設計的初衷是相悖的, 我們想要最終能夠追趕上 Node1 節點數據, 那麼這個時候就會採取同步與異步複製之間的折衷方案, 即部分採用同步複製, 部分採用異步複製.
- 半同步複製
既然我們想讓數據副本能夠保證與 Node1 節點一致, 那麼我們就在同步與異步複製方式之間進行取捨, 即 Node1 節點同步複製到 Node2 節點, 而 Node3 節點則是異步方式進行復制, 即如下:
那麼這個時候如果 Node1 節點發生不可用, 我們直接用 Node2 節點頂上, 這個時候就能夠保證我們的數據不丟失. 但是我們把讀寫請求都落到了單個節點上, 無法充分利用其他數據副本節點資源, 如果我既想讓客戶端寫入就讀取到最新數據, 又想充分利用資源節點呢?
- 多數複製
這個時候我們會採用大多數複製方式來實現, 如下:
這個時候我們就可以實現數據強一致性, 同時可用性以及故障容忍度也較高, 還能充分利用冗餘節點資源, 但是它沒有缺點嗎? 肯定是有代價, 一是寫入性能低, 因爲需要等待集羣半數以上節點返回才響應給客戶端; 二是實現複雜, 尤其是實現共識算法一致性, 需要在數據同步過程保證其安全性屬性, 而且還會面臨不可靠時鐘以及網絡問題, Node1 節點需要就相同的數據複製提案保證原子性, 實現成本比較高.
總結
關於數據複製原理, 其一是複製格式, 對於複製數據的格式我們要考慮到數據安全性問題, 如果損壞了數據的原有安全屬性, 我們要實現冗餘數據副本的目標就不功自破; 其次是數據複製方式, 需要基於一致性模型根據系統實際情況進行 Trade-Off.
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/fn7K3BFwRD8cg6EpvbMe8w