本文主要聊一聊主流開源產品的replication方式。node
replication和partition/sharding是分佈式系統必備的兩種能力。具體詳見複製、分片和路由.
對於海量數據來講,replication一方面能夠增長冗餘,保證系統可用性,一方面還能夠提高讀取的效率。
本文主要聚焦於replication,即假設每一個node都足以存下整個副本。mysql
按照有無leader以及leader數目能夠分爲:redis
single leader replication
即一主多從的複製方式,由leader同步/通知follower,只有leader能接受寫操做,follower只能讀不能寫。算法
multi leader replication
即多主多從,有多個leader分佈在不一樣node,同時接受寫入操做,而每一個leader之間相互爲follower。比較適合多數據中心的場景,不過對於併發寫多數據中心衝突解決的複雜度也增長。sql
leaderless replication
無中心的複製,不區分主從副本,任意節點均可以接收請求,而後又它去通知其餘副本進行更新。數據庫
具體詳見副本更新策略,主要有以下幾種segmentfault
無中心的複製,能夠分爲三種拓撲結構,環形、星/樹型、網狀拓撲緩存
主要分爲如下幾種併發
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增長冗餘經常使用來作master的的熱備(支持查詢)/溫備(不支持查詢)
當舊的master恢復的時候,這個時候就涉及舊master與新master之間的數據差別的處理
一旦replication支持讀取的話,那麼就涉及讀的一致性問題,通常理論上除了強一致外,有這幾種最終一致性:
(1)因果一致性(Causal consistency)
即進程A在更新完數據後通知進程B,那麼以後進程B對該項數據的範圍都是進程A更新後的最新值。
讀取的話,涉及讀己所寫,因果讀(
針對操做有序
)、單調讀(不讀到舊數據
)
假設某份數據須要複製到3個節點,爲了保證強一致性,不須要全部節點都確認寫入操做,只須要其中兩個節點(也就是超半數節點)確認就能夠了。在這種狀況下,若是發生兩個相互衝突的寫入操做,那麼只有其中一個操做能爲超過半數的節點所承認,這就是寫入仲裁(write quorum),若是用稍微正規一點的方式說,那就是W>N/2,這個不等式的意思是參與寫入操做的節點數W,必須超過副本節點數N的一半,副本節點數又稱爲複製因子(replication factor)。
讀取仲裁(read quorum),也就是說想保證可以讀到最新的數據,必須與多少個節點聯繫才行。假設寫入操做須要兩個節點來確認(W=2),那麼咱們至少得聯繫兩個節點,才能保證獲取到最新數據。然而,假如某些寫入操做只被一個節點所確認(W=1),那麼咱們就必須3個節點都通訊一遍,才能確保獲取到的數據是最新的。一個狀況下,因爲寫入操做沒有得到足夠的節點支持率,因此可能會產生更新衝突。可是,只要從足夠數量的節點中讀出數據,就必定能偵測出此類衝突。所以,即便在寫入操做不具有強一致性的狀況下,也能夠實現除具備強一致性的讀取操做來。
這三者之間的關係,能夠用一個不等式來表述,即只有當R+W>N的時候,才能保證讀取操做的強一致性。
產品 | 複製方式 | 實現方式 | 其餘 |
---|---|---|---|
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,通常是增量同步
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中刪除。
因而可知,Kafka的複製機制既不是徹底的同步複製,也不是單純的異步複製。事實上,同步複製要求全部能工做的follower都複製完,這條消息纔會被commit,這種複製方式極大的影響了吞吐率。而異步複製方式下,follower異步的從leader複製數據,數據只要被leader寫入log就被認爲已經commit,這種狀況下若是follower都尚未複製完,落後於leader時,忽然leader宕機,則會丟失數據。而Kafka的這種使用ISR的方式則很好的均衡了確保數據不丟失以及吞吐率。
es的一致性主要有兩個方面:
使用lucene索引機制帶來的refresh問題
在 Elasticsearch 中,寫入和打開一個新段的輕量的過程叫作 refresh 。 默認狀況下每一個分片會每秒自動刷新一次。這就是爲何咱們說 Elasticsearch是近實時搜索: 文檔的變化並非當即對搜索可見,但會在一秒以內變爲可見。
這些行爲可能會對新用戶形成困惑: 他們索引了一個文檔而後嘗試搜索它,但卻沒有搜到。這個問題的解決辦法是用 refresh API 執行一次手動刷新.
refresh_interval 能夠在既存索引上進行動態更新。 在生產環境中,當你正在創建一個大的新索引時,能夠先關閉自動刷新,待開始使用該索引時,再把它們調回來.
使用分片和複製帶來的副本一致性問題(consistency:one、all、quorum)
在有副本配置的狀況下,數據從發向Elasticsearch節點,到接到Elasticsearch節點響應返回,流向以下
不一樣產品的replication細節不盡相同,可是大的理論是一致的,對於replication除了關注上述的replication相關方式外,還須要額外關注replication相關異常場景,才能作到成熟應用。