原創:小姐姐味道(微信公衆號ID:xjjdog),歡迎分享,轉載請保留出處。mysql
分佈式系統,經過數據冗餘,來保證數據的安全。要寫一個分佈式系統,一道繞不過去的坎,那就是數據同步。程序員
同步,這兩個字,折磨死了不少人。web
是同步,仍是異步?是push,仍是pull?誰是master,誰是slave?下線會怎樣,上線了又會怎樣?中心化,or對等節點?redis
這些問題,無一不拷打者分佈式系統的設計者。算法
下面,咱們將看一下主流的幾個存儲服務,是如何解決數據同步問題的。sql
mysql的主服務器叫作master,從服務器叫作slave。mongodb
主服務器將變動記錄在binlog中,slave將經過獨立的線程拷貝這些記錄,而後重放。數據庫
binlog的格式分爲statement、row、mixed三種。緩存
因爲是異步線程去拷貝,slave很容易會出現延遲。當master不幸宕機,將會形成延遲的數據丟失。安全
爲了解決異步複製的問題,5.5版本以後,MySQL引入了半同步複製(semi sync)的概念。半同步處於異步和全量同步之間,master執行完事務以後,並不直接返回,而是要等待至少一個slave寫入成功才返回。因爲須要與至少一個slave進行交互,性能相比較異步複製確定是有很多折損的。
全複製模式固然是要等待全部的slave節點複製完成,這種安全性最高,可是效率也最低。從概念上來說,只有一個slave的半複製就是全複製。
5.7以後,mysql實現了組複製(group replication)協議。它支持單主模式和多主模式,但在同一個group內,不容許同時存在。聽起還好像很神奇,其實它仍是經過paxos協議去實現的。
kafka因爲是一個消息隊列,因此不須要考慮隨機刪除和隨機更新的問題,它只關注寫入問題便可。從結構上來講,kafka的同步單元是很是分散的:kafka有多個topic,每一個topic又分爲多個partition,副本就是基於partiton去作的。
主分區叫作leader,1-n個副本叫作follower。生產者在發送消息的時候,須要先找到該分區的leader,而後將數據發送給它。follower只是做爲一個備份存在,以便在主分區發生問題時可以頂上去。
kafka的主從同步,叫作ISR(In Sync Replica)機制。
那何時消息算是發送成功呢?這還要看ack的發送級別。
0
表示異步發送,消息發送完畢就算是成功了1
leader主副本寫入完成,就算是發送成功了-1
leader發送完成,而且ISR中的副本都須要回覆ack0和1的狀況下,kafka都有丟失消息的可能。在-1的狀況下,也須要保證至少有一個follower commit成功才能保證消息安全。若是follower都不能追遇上leader,則會被移除出 ISR列表。沒錯,是直接移除。當ISR爲空,則kafka的分區和單機是沒有區別的,因此kafka提供了min.insync.replicas
參數規定了最小ISR。
副本之間的數據複製,是經過follower pull
的方式,也就是拉取的方式去獲取的。
redis是內存kv數據庫,速度上遠超其餘數據庫,理論上主從同步更容易。但在高流量和高QPS下,主從複製依然會發生問題。
redis的slave鏈接上以後,首先會進行一次全量同步。它會發送psync命令到master,而後master執行bgsave生成一個rdb文件。全量同步就是複製這個rdb快照文件到slave。
那在全量複製中間出現的數據怎麼辦呢?確定是要緩存起來的。master會開啓一個buffer,而後記錄全量複製過程當中產生的新數據,在全量同步完成以後再補齊增量數據。
slave斷線以後也不須要每次都執行全量同步,爲了配合增量,還引入了複製偏移量(offset)
、複製積壓緩衝區(replication backlog buffer)
和運行 ID (run_id)
三個概念。能夠看出它都是爲了標識slave,以及它的複製位置和緩衝區用的。
以後的同步,就能夠一直使用psync去複製。依然是異步複製。
能夠看出redis的主從複製一致性大量依賴內存,級別是很是弱的。可是它快。快能解決不少問題,因此應用場景是不一樣的。
es是基於lucene的搜索引擎,數據節點會包含多個索引(index)。每一個索引包含多個分片(shard),每一個分片又包含多個replica(副本)。
從上面的描述來看,這些概念是與kafka高度雷同的,es的複製單元是分片。
es的數據依然是先寫master,它一樣維護了一個同步中的slave列表(InSyncAllocationIds),處於yellow和red狀態的副本固然是不在這個列表中的。
master須要等待全部這些正常的副本寫入完成後,才返回給客戶端,因此一致性級別是比較高的,由於它的slave節點是要參與讀操做的,它是一個近實時系統。
因爲它是一個數據庫,因此依然會有刪除和更新操做。Translog至關於wal日誌,保證了斷電的數據安全,這和其餘rdbms的套路是一致的。
cassandra是一個很是有名的CAP理論實踐數據庫,更多的像一個AP數據庫,目前在db-engines.com依然有較高的排名。
數據存儲是表的概念,一個表能夠存儲在多臺機器上。它的分區,是經過partition key來設計的,數據分佈很是依賴於hash函數。若是某個節點出現問題怎麼辦?那就須要一致性hash的支持。
cassandra很是有意思,它的複製(replicas)並不像其餘的主備數據同樣,它更像是多份master數據,這些數據都是同時向外提供服務的。當掉一個檢點,並不須要主備切換。
爲何能夠作到這種程度呢?由於cassandra追求的是最終一致性。分佈式系統因爲副本的存在,不可避免的要異步或者同步複製。那到底複製到什麼程度纔算是合適的呢?Quorum
的R+W
就是一個權衡策略。
quorum = (sum_of_replication_factors / 2) + 1
複製代碼
什麼意思呢?考慮到你有5個抽屜,而後隨機放入W個球,求須要多少次R,才能拿出一個球。假如你向裏面放了1個球,你須要打開5次,才能每次都有正確的判斷,此時R=五、W=1;當你放了2個球,則你只須要打開4次就能夠了;假如你放入了5個球,那就只須要讀一次。
當R+W>N的時候,屬於強一致性;當R+W<=N的時候,屬於最終一致性。
有意思的是,cassandra中的集羣信息,即meta信息,使用gossip(push-pull-gossip)進行傳遞。
mongodb有三種數據冗餘方式。一種是master-slave(不推薦使用),一種是replica set,一種是 sharding模式。
mongodb的副本集主從,就是標準的故障自動轉移實現方式,不須要人工介入。master節點當掉以後,會經過選舉從副本集中找出新的master節點,而後引導其餘節點鏈接到這個master。
mongodb的選舉算法,採用的是bully。
主節點的變動,會存放在特定的系統表中。slave會定時拉取這些變動,並應用。從這種描述中也能夠看出,mongodb在同步延遲或者單節點出問題的時候,會有丟失數據的可能。
分佈式是爲了解決單機的容量問題,但它引入了一個新的問題,那就是數據同步。
數據同步要關注一致性,故障恢復以及時效性。
主要有兩種數據須要同步。
對於元數據信息,目前比較主流的作法,能夠參考使用raft協議進行數據分發。到了真正的數據同步方面,raft協議的效率仍是有些低的,因此會廣泛採用異步複製的方式。
在這種狀況下,異步複製列表,就成了關鍵的元數據信息,集羣須要維護這些節點的狀態。最壞的狀況下,異步複製節點所有不可用,master會本身運行在很是不可信的環境下。
爲了增長數據分配的靈活性,這些複製單元多會針對於sharding分片進行操做,由此帶來的,就是meta信息的爆炸。
分佈式系統這麼多,但並無一個可以統一的模式。有意思的是,即便是最低效的分佈式系統,也有大批的追隨者。不信?看看BTC的走勢就知道了。
做者簡介:小姐姐味道 (xjjdog),一個不容許程序員走彎路的公衆號。聚焦基礎架構和Linux。十年架構,日百億流量,與你探討高併發世界,給你不同的味道。個人我的微信xjjdog0,歡迎添加好友,進一步交流。