聊聊replication的方式

本文主要聊一聊主流開源產品的replication方式。node

replication

replication和partition/sharding是分佈式系統必備的兩種能力。具體詳見複製、分片和路由.
對於海量數據來講,replication一方面能夠增長冗餘,保證系統可用性,一方面還能夠提高讀取的效率。
本文主要聚焦於replication,即假設每一個node都足以存下整個副本。mysql

replication type

按照有無leader以及leader數目能夠分爲:redis

  • single leader replication
    即一主多從的複製方式,由leader同步/通知follower,只有leader能接受寫操做,follower只能讀不能寫。算法

  • multi leader replication
    即多主多從,有多個leader分佈在不一樣node,同時接受寫入操做,而每一個leader之間相互爲follower。比較適合多數據中心的場景,不過對於併發寫多數據中心衝突解決的複雜度也增長。sql

  • leaderless replication
    無中心的複製,不區分主從副本,任意節點均可以接收請求,而後又它去通知其餘副本進行更新。數據庫

leader-based replication way

具體詳見副本更新策略,主要有以下幾種segmentfault

  • sync replication
    同步複製,這個能夠保證強一致,不過follower多的狀況下,延遲太大,通常不多使用
  • async replication
    異步複製,這個可能形成讀不一致,可是寫入效率高
  • semi sync replication
    半同步,通常採用quorum的機制,即當寫入的節點個數知足指定條件,即算寫入成功,而後經過併發請求多個node來知足讀取的一致性

leaderless replication way

無中心的複製,能夠分爲三種拓撲結構,環形、星/樹型、網狀拓撲緩存

replication implementation

主要分爲如下幾種併發

  • statement/trigger-based replication
    這種是基於數據庫的語句或觸發器來實現的複製,可是存在必定的問題,好比一些now()/rand()/seq()等函數可能形成主從同步的不肯定性,好比從節點的now()/rand()等執行結果跟master不同。mysql5.1版本以前用的是這種,5.1+版本,當有不肯定語句時,就切換爲row-based log replicationless

  • write-ahead-log replication(WAL)
    WAL是數據庫中一種高效的日誌算法,對於非內存數據庫而言,磁盤I/O操做是數據庫效率的一大瓶頸。在相同的數據量下,採用WAL日誌的數據庫系統在事務提交時,磁盤寫操做只有傳統的回滾日誌的一半左右,大大提升了數據庫磁盤I/O操做的效率,從而提升了數據庫的性能。
    PG使用的就是這種。

  • row-based-log replication(logical log)
    WAL跟數據庫存儲引擎是耦合的,而row-based-log也稱做logical log,是跟存儲引擎無關的,採用的是change data capture的方式,這個就很方便異構數據源的數據同步。

replication帶來的問題

replication lag

  • 同步差別大
    好比mongo的oplog過小,跟不上寫入速度,形成舊的操做日誌就會被丟棄,主從延遲一直增長致使副本同步失敗。
  • 新加入node的同步
    好比在線擴容增長replication,這個時候就涉及新節點的node的replication問題,通常這類同步的方式跟正常在線節點的同步方式是分開的,新的node同步到必定的時候才轉爲正常的增量同步方式。

master slave failover

通常replication增長冗餘經常使用來作master的的熱備(支持查詢)/溫備(不支持查詢)

  • 當主節點掛的時候,這個時候就涉及選哪一個replication爲主的問題
  • 當舊的master恢復的時候,這個時候就涉及舊master與新master之間的數據差別的處理

    read consistency

    一旦replication支持讀取的話,那麼就涉及讀的一致性問題,通常理論上除了強一致外,有這幾種最終一致性:

  • (1)因果一致性(Causal consistency)
    即進程A在更新完數據後通知進程B,那麼以後進程B對該項數據的範圍都是進程A更新後的最新值。

  • (2)讀己之所寫(Read your writes)
    進程A更新一項數據後,它本身老是能訪問到本身更新過的最新值。
  • (3)會話一致性(Session consistency)
    將數據一致性框定在會話當中,在一個會話當中實現讀己之所寫的一致性。即執行更新後,客戶端在同一個會話中始終能讀到該項數據的最新值
  • (4)單調讀一致性(Monotonic read consistency)
    若是一個進程從系統中讀取出一個數據項的某個值後,那麼系統對於該進程後續的任何數據訪問都不該該返回更舊的值。
  • (5)單調寫一致性(Monotoic write consistency)
    一個系統須要保證來自同一個進程的寫操做被順序執行。

讀取的話,涉及讀己所寫,因果讀(針對操做有序)、單調讀(不讀到舊數據)

quorum/RWN方案解決讀衝突

write quorum

假設某份數據須要複製到3個節點,爲了保證強一致性,不須要全部節點都確認寫入操做,只須要其中兩個節點(也就是超半數節點)確認就能夠了。在這種狀況下,若是發生兩個相互衝突的寫入操做,那麼只有其中一個操做能爲超過半數的節點所承認,這就是寫入仲裁(write quorum),若是用稍微正規一點的方式說,那就是W>N/2,這個不等式的意思是參與寫入操做的節點數W,必須超過副本節點數N的一半,副本節點數又稱爲複製因子(replication factor)。

read quorum

讀取仲裁(read quorum),也就是說想保證可以讀到最新的數據,必須與多少個節點聯繫才行。假設寫入操做須要兩個節點來確認(W=2),那麼咱們至少得聯繫兩個節點,才能保證獲取到最新數據。然而,假如某些寫入操做只被一個節點所確認(W=1),那麼咱們就必須3個節點都通訊一遍,才能確保獲取到的數據是最新的。一個狀況下,因爲寫入操做沒有得到足夠的節點支持率,因此可能會產生更新衝突。可是,只要從足夠數量的節點中讀出數據,就必定能偵測出此類衝突。所以,即便在寫入操做不具有強一致性的狀況下,也能夠實現除具備強一致性的讀取操做來。

RWN

  • R
    執行讀取操做時所需聯繫的節點數R
  • W
    確認寫入操做時所需徵詢的節點數W
  • N
    複製因子N

這三者之間的關係,能夠用一個不等式來表述,即只有當R+W>N的時候,才能保證讀取操做的強一致性。

主流開源產品的replication概覽

產品 複製方式 實現方式 其餘
mysql 主從半同步 MySQL 5.0及以前的版本僅支持statement-based的複製,5.1+版本,當有不肯定語句時,就切換爲row-based log replication 主從延遲處理
kafka 主從ISR半同步 leader寫入消息並複製到全部follower,ISR中的副本寫入成功返回ack給leader纔算commit成功 生產者能夠選擇是否等待ISR的ack
elasticsearch 主從半同步,默認replication=sync consistency可選的值有quorum、one和all。默認的設置爲quorum tradelog及fsync以及refresh
pg 主從異步複製 基於Write-ahead log archive及stream方式
redis 主從異步複製 增量Redis Protocol(全量\增量\長鏈接) Sentinel failover
mongo 主從異步,Replica set模式 持久化的ring-buffer local.oplog.rs(initial_sync,steady-sync) Arbiter選主

能夠看見一些對一致性要求高的,能夠採用半同步的機制,通常是基於quorum機制,像es就是基於這種機制,而kafka是採用ISR機制,兩者均可以配置
其餘的基本是異步複製,對於新加入的node以及recovery node的同步來講,採用不一樣的同步方式,新加入的通常採用全量同步,而處於正常狀態的node,通常是增量同步

kafka的ISR(In-Sync Replicas的縮寫,表示副本同步隊列)

全部的副本(replicas)統稱爲Assigned Replicas,即AR。ISR是AR中的一個子集,由leader維護ISR列表,follower從leader同步數據有一些延遲,任意一個超過閾值都會把follower剔除出ISR,存入OSR(Outof-Sync Replicas)列表,新加入的follower也會先存放在OSR中。AR=ISR+OSR。

當producer發送一條消息到broker後,leader寫入消息並複製到全部follower。消息提交以後才被成功複製到全部的同步副本。消息複製延遲受最慢的follower限制,重要的是快速檢測慢副本,若是follower「落後」太多或者失效,leader將會把它從ISR中刪除。

ISR以及HW和LEO的流轉過程
ISR以及HW和LEO的流轉過程

因而可知,Kafka的複製機制既不是徹底的同步複製,也不是單純的異步複製。事實上,同步複製要求全部能工做的follower都複製完,這條消息纔會被commit,這種複製方式極大的影響了吞吐率。而異步複製方式下,follower異步的從leader複製數據,數據只要被leader寫入log就被認爲已經commit,這種狀況下若是follower都尚未複製完,落後於leader時,忽然leader宕機,則會丟失數據。而Kafka的這種使用ISR的方式則很好的均衡了確保數據不丟失以及吞吐率。

es的副本一致性

es的一致性主要有兩個方面:

  • 使用lucene索引機制帶來的refresh問題


    在Elasticsearch和磁盤之間是文件系統緩存。 在內存索引緩衝區中的文檔會被寫入到一個新的段中,可是這裏新段會被先寫入到文件系統緩存--這一步代價會比較低,稍後再被刷新到磁盤--這一步代價比較高。不過只要文件已經在緩存中, 就能夠像其它文件同樣被打開和讀取了。

    在 Elasticsearch 中,寫入和打開一個新段的輕量的過程叫作 refresh 。 默認狀況下每一個分片會每秒自動刷新一次。這就是爲何咱們說 Elasticsearch是近實時搜索: 文檔的變化並非當即對搜索可見,但會在一秒以內變爲可見。
    這些行爲可能會對新用戶形成困惑: 他們索引了一個文檔而後嘗試搜索它,但卻沒有搜到。這個問題的解決辦法是用 refresh API 執行一次手動刷新.
    refresh_interval 能夠在既存索引上進行動態更新。 在生產環境中,當你正在創建一個大的新索引時,能夠先關閉自動刷新,待開始使用該索引時,再把它們調回來.

  • 使用分片和複製帶來的副本一致性問題(consistency:one、all、quorum)
    在有副本配置的狀況下,數據從發向Elasticsearch節點,到接到Elasticsearch節點響應返回,流向以下

  • 1)客戶端請求發送給Node1節點,這裏也能夠發送給其餘節點
  • 2)Node1節點用數據的_id計算出數據應該存儲在shard0上,經過cluster state信息發現shard0的主分片在Node3節點上,Node1轉發請求數據給Node3,Node3完成數據的索引,索引過程在上篇博客中詳細介紹了。
  • 3)Node3並行轉發數據給分配有shard0的副本分片Node1和Node2上。當收到任一節點彙報副本分片數據寫入成功之後,Node3即返回給初始的接受節點Node1,宣佈數據寫入成功。Node1成功返回給客戶端。

小結

不一樣產品的replication細節不盡相同,可是大的理論是一致的,對於replication除了關注上述的replication相關方式外,還須要額外關注replication相關異常場景,才能作到成熟應用。

doc

相關文章
相關標籤/搜索