分布式系統(tǒng)咋做同步?虐死人!
本文轉載自微信公眾號「小姐姐味道」,作者小姐姐養(yǎng)的狗 。轉載本文請聯(lián)系小姐姐味道公眾號。
分布式系統(tǒng),通過數(shù)據(jù)冗余,來保證數(shù)據(jù)的安全。要寫一個分布式系統(tǒng),一道繞不過去的坎,那就是數(shù)據(jù)同步。
同步,這兩個字,折磨死了很多人。
是同步,還是異步?是push,還是pull?誰是master,誰是slave?下線會怎樣,上線了又會怎樣?中心化,or對等節(jié)點?
這些問題,無一不拷打者分布式系統(tǒng)的設計者。
下面,我們將看一下主流的幾個存儲服務,是如何解決數(shù)據(jù)同步問題的。
MySQL如何做主從同步?
mysql的主服務器叫做master,從服務器叫做slave。
主服務器將變更記錄在binlog中,slave將通過獨立的線程拷貝這些記錄,然后重放。
binlog的格式分為statement、row、mixed三種。
- statement 將變更的sql語句寫入到binlog中,在準確性方面會有一定影響
- row 將每一條記錄的變化,寫入到binlog中
- mixed 上面兩種的結合。MySQL會判斷什么時候有用statement,什么時候用row
由于是異步線程去拷貝,slave很容易會出現(xiàn)延遲。當master不幸宕機,將會造成延遲的數(shù)據(jù)丟失。
為了解決異步復制的問題,5.5版本之后,MySQL引入了半同步復制(semi sync)的概念。半同步處于異步和全量同步之間,master執(zhí)行完事務之后,并不直接返回,而是要等待至少一個slave寫入成功才返回。由于需要與至少一個slave進行交互,性能相比較異步復制肯定是有不少折損的。
全復制模式當然是要等待所有的slave節(jié)點復制完成,這種安全性最高,但是效率也最低。從概念上來講,只有一個slave的半復制就是全復制。
5.7之后,mysql實現(xiàn)了組復制(group replication)協(xié)議。它支持單主模式和多主模式,但在同一個group內(nèi),不允許同時存在。聽起還好像很神奇,其實它還是通過paxos協(xié)議去實現(xiàn)的。
Kafka如何做的副本同步?
kafka由于是一個消息隊列,所以不需要考慮隨機刪除和隨機更新的問題,它只關注寫入問題即可。從結構上來說,kafka的同步單元是非常分散的:kafka有多個topic,每個topic又分為多個partition,副本就是基于partiton去做的。
主分區(qū)叫做leader,1-n個副本叫做follower。生產(chǎn)者在發(fā)送消息的時候,需要先找到該分區(qū)的leader,然后將數(shù)據(jù)發(fā)送給它。follower只是作為一個備份存在,以便在主分區(qū)發(fā)生問題時能夠頂上去。
kafka的主從同步,叫做ISR(In Sync Replica)機制。
那什么時候消息算是發(fā)送成功呢?這還要看ack的發(fā)送級別。
- 0 表示異步發(fā)送,消息發(fā)送完畢就算是成功了
- 1 leader主副本寫入完成,就算是發(fā)送成功了
- -1 leader發(fā)送完成,并且ISR中的副本都需要回復ack
0和1的情況下,kafka都有丟失消息的可能。在-1的情況下,也需要保證至少有一個follower commit成功才能保證消息安全。如果follower都不能追趕上leader,則會被移除出 ISR列表。沒錯,是直接移除。當ISR為空,則kafka的分區(qū)和單機是沒有區(qū)別的,所以kafka提供了min.insync.replicas參數(shù)規(guī)定了最小ISR。
- 當ISR不滿足的時候怎么辦?kafka當然是不會丟失消息了,因為此時生產(chǎn)者的提交是失敗的,消息根本進不了系統(tǒng)里來
- 當所有副本都不可用怎么辦?此時,該partition將永不可用
副本之間的數(shù)據(jù)復制,是通過follower pull的方式,也就是拉取的方式去獲取的。
Redis的主從復制
redis是內(nèi)存kv數(shù)據(jù)庫,速度上遠超其他數(shù)據(jù)庫,理論上主從同步更容易。但在高流量和高QPS下,主從復制依然會發(fā)生問題。
redis的slave連接上之后,首先會進行一次全量同步。它會發(fā)送psync命令到master,然后master執(zhí)行bgsave生成一個rdb文件。全量同步就是復制這個rdb快照文件到slave。
那在全量復制中間出現(xiàn)的數(shù)據(jù)怎么辦呢?肯定是要緩存起來的。master會開啟一個buffer,然后記錄全量復制過程中產(chǎn)生的新數(shù)據(jù),在全量同步完成之后再補齊增量數(shù)據(jù)。
slave斷線之后也不需要每次都執(zhí)行全量同步,為了配合增量,還引入了復制偏移量(offset)、復制積壓緩沖區(qū)(replication backlog buffer)和運行 ID (run_id)三個概念??梢钥闯鏊际菫榱藰俗Rslave,以及它的復制位置和緩沖區(qū)用的。
之后的同步,就可以一直使用psync去復制。依然是異步復制。
可以看出redis的主從復制一致性大量依賴內(nèi)存,級別是非常弱的。但是它快??炷芙鉀Q很多問題,所以應用場景是不同的。
ElasticSearch主從復制
es是基于lucene的搜索引擎,數(shù)據(jù)節(jié)點會包含多個索引(index)。每個索引包含多個分片(shard),每個分片又包含多個replica(副本)。
從上面的描述來看,這些概念是與kafka高度雷同的,es的復制單元是分片。
es的數(shù)據(jù)依然是先寫master,它同樣維護了一個同步中的slave列表(InSyncAllocationIds),處于yellow和red狀態(tài)的副本當然是不在這個列表中的。
master需要等待所有這些正常的副本寫入完成后,才返回給客戶端,所以一致性級別是比較高的,因為它的slave節(jié)點是要參與讀操作的,它是一個近實時系統(tǒng)。
由于它是一個數(shù)據(jù)庫,所以依然會有刪除和更新操作。Translog相當于wal日志,保證了斷電的數(shù)據(jù)安全,這和其他rdbms的套路是一致的。
Cassandra集群模式
cassandra是一個非常有名的CAP理論實踐數(shù)據(jù)庫,更多的像一個AP數(shù)據(jù)庫,目前在db-engines.com依然有較高的排名。
數(shù)據(jù)存儲是表的概念,一個表可以存儲在多臺機器上。它的分區(qū),是通過partition key來設計的,數(shù)據(jù)分布非常依賴于hash函數(shù)。如果某個節(jié)點出現(xiàn)問題怎么辦?那就需要一致性hash的支持。
cassandra非常有意思,它的復制(replicas)并不像其他的主備數(shù)據(jù)一樣,它更像是多份master數(shù)據(jù),這些數(shù)據(jù)都是同時向外提供服務的。當?shù)粢粋€檢點,并不需要主備切換。
為什么可以做到這種程度呢?因為cassandra追求的是最終一致性。分布式系統(tǒng)由于副本的存在,不可避免的要異步或者同步復制。那到底復制到什么程度才算是合適的呢?Quorum的R+W就是一個權衡策略。
- quorum = (sum_of_replication_factors / 2) + 1
什么意思呢?考慮到你有5個抽屜,然后隨機放入W個球,求需要多少次R,才能拿出一個球。假如你向里面放了1個球,你需要打開5次,才能每次都有正確的判斷,此時R=5、W=1;當你放了2個球,則你只需要打開4次就可以了;假如你放入了5個球,那就只需要讀一次。
當R+W>N的時候,屬于強一致性;當R+W<=N的時候,屬于最終一致性。
有意思的是,cassandra中的集群信息,即meta信息,使用gossip(push-pull-gossip)進行傳遞。
MongoDB主從復制
mongodb有三種數(shù)據(jù)冗余方式。一種是master-slave(不推薦使用),一種是replica set,一種是 sharding模式。
mongodb的副本集主從,就是標準的故障自動轉移實現(xiàn)方式,不需要人工介入。master節(jié)點當?shù)糁?,會通過選舉從副本集中找出新的master節(jié)點,然后引導其他節(jié)點連接到這個master。
mongodb的選舉算法,采用的是bully。
主節(jié)點的變更,會存放在特定的系統(tǒng)表中。slave會定時拉取這些變更,并應用。從這種描述中也可以看出,mongodb在同步延遲或者單節(jié)點出問題的時候,會有丟失數(shù)據(jù)的可能。
總結
分布式是為了解決單機的容量問題,但它引入了一個新的問題,那就是數(shù)據(jù)同步。
數(shù)據(jù)同步要關注一致性,故障恢復以及時效性。
主要有兩種數(shù)據(jù)需要同步。
- 元數(shù)據(jù)信息
- 真正的數(shù)據(jù)
對于元數(shù)據(jù)信息,目前比較主流的做法,可以參考使用raft協(xié)議進行數(shù)據(jù)分發(fā)。到了真正的數(shù)據(jù)同步方面,raft協(xié)議的效率還是有些低的,所以會普遍采用異步復制的方式。
在這種情況下,異步復制列表,就成了關鍵的元數(shù)據(jù)信息,集群需要維護這些節(jié)點的狀態(tài)。最壞的情況下,異步復制節(jié)點全部不可用,master會自己運行在非常不可信的環(huán)境下。
為了增加數(shù)據(jù)分配的靈活性,這些復制單元多會針對于sharding分片進行操作,由此帶來的,就是meta信息的爆炸。
分布式系統(tǒng)這么多,但并沒有一個能夠統(tǒng)一的模式。有意思的是,即使是最低效的分布式系統(tǒng),也有大批的追隨者。不信?看看BTC的走勢就知道了。
作者簡介:小姐姐味道 (xjjdog),一個不允許程序員走彎路的公眾號。聚焦基礎架構和Linux。十年架構,日百億流量,與你探討高并發(fā)世界,給你不一樣的味道。