canal mysql从库_大厂如何基于binlog解决多机房同步mysql数据(一)?
目錄
前言
小伙伴們是否經(jīng)常聽說多機(jī)房部署,異地容災(zāi)?什么兩地3中心,三地5中心?是否好奇多機(jī)房部署,數(shù)據(jù)之間是如何共享的呢?
今天老顧就來嘗試著給大家解惑解惑,并詳細(xì)介紹一下數(shù)據(jù)同步的問題。
單一IDC
上圖的架構(gòu),是一個(gè)IDC機(jī)房中,部署了一主兩從mysql數(shù)據(jù)庫集群,大多數(shù)據(jù)中小型互聯(lián)網(wǎng)公司采用的方案。
上面的方案存在一些問題:
1)不同地區(qū)的用戶體驗(yàn)速度不同。一個(gè)IDC必然只能部署在一個(gè)地區(qū),例如部署在北京,那么北京的用戶訪問將會(huì)得到快速響應(yīng);但是對于上海的用戶,訪問延遲一般就會(huì)大一點(diǎn)。
上海到北京的一個(gè)RTT可能有20ms左右。
2)容災(zāi)問題。這里容災(zāi)不是單臺(tái)機(jī)器故障,而是指機(jī)房斷電,自然災(zāi)害,或者光纖被挖斷等重大災(zāi)害。一旦出現(xiàn)這種問題,將無法正常為用戶提供訪問,甚至出現(xiàn)數(shù)據(jù)丟失的情況。
某年,支付寶杭州某數(shù)據(jù)中心的光纜就被挖斷過
多IDC
為了解決這些問題,我們可以將服務(wù)部署到多個(gè)不同的IDC中,不同IDC之間的數(shù)據(jù)互相進(jìn)行同步。如下圖
通過這種方式,我們可以解決單機(jī)房遇到的問題:
1)用戶體驗(yàn)。不同的用戶可以選擇離自己最近的機(jī)房進(jìn)行訪問
2)容災(zāi)問題。當(dāng)一個(gè)機(jī)房掛了之后,我們可以將這個(gè)機(jī)房用戶的流量調(diào)度到另外一個(gè)正常的機(jī)房,由于不同機(jī)房之間的數(shù)據(jù)是實(shí)時(shí)同步的,用戶流量調(diào)度過去后,也可以正常訪問數(shù)據(jù)
故障發(fā)生那一刻的少部分?jǐn)?shù)據(jù)可能會(huì)丟失
關(guān)于流量的調(diào)度問題,本文就不介紹,以后老顧會(huì)單獨(dú)介紹流量、灰度發(fā)布的問題。本文主要介紹數(shù)據(jù)同步。
容災(zāi)補(bǔ)充
- 機(jī)房容災(zāi) : 上面的案例中,我們使用了2個(gè)IDC,但是2個(gè)IDC并不能具備機(jī)房容災(zāi)能力。至少需要3個(gè)IDC,例如,一些基于多數(shù)派協(xié)議的一致性組件,如zookeeper,redis、etcd、consul等,需要得到大部分節(jié)點(diǎn)的同意。例如我們部署了3個(gè)節(jié)點(diǎn),在只有2個(gè)機(jī)房的情況下, 必然是一個(gè)機(jī)房部署2個(gè)節(jié)點(diǎn),一個(gè)機(jī)房部署一個(gè)節(jié)點(diǎn)。當(dāng)部署了2個(gè)節(jié)點(diǎn)的機(jī)房掛了之后,只剩下一個(gè)節(jié)點(diǎn),無法形成多數(shù)派。在3機(jī)房的情況下,每個(gè)機(jī)房部署一個(gè)節(jié)點(diǎn),任意一個(gè)機(jī)房掛了,還剩2個(gè)節(jié)點(diǎn),還是可以形成多數(shù)派。這也就是我們常說的"兩地三中心”。
- 城市級(jí)容災(zāi):在發(fā)生重大自然災(zāi)害的情況下,可能整個(gè)城市的機(jī)房都無法訪問。為了達(dá)到城市級(jí)容災(zāi)的能力,使用的是"三地五中心"的方案。這種情況下,3個(gè)城市分別擁有2、2、1個(gè)機(jī)房。當(dāng)整個(gè)城市發(fā)生災(zāi)難時(shí),其他兩個(gè)城市依然至少可以保證有3個(gè)機(jī)房依然是存活的,同樣可以形成多數(shù)派。
Mysql主從同步
小伙伴們應(yīng)該知道m(xù)ysql的主從架構(gòu)的數(shù)據(jù)復(fù)制的基本原理
通常一個(gè)mysql集群有一主多從構(gòu)成。用戶的數(shù)據(jù)都是寫入主庫Master,Master將數(shù)據(jù)寫入到本地二進(jìn)制日志binary log中。從庫Slave啟動(dòng)一個(gè)IO線程(I/O Thread)從主從同步binlog,寫入到本地的relay log中,同時(shí)slave還會(huì)啟動(dòng)一個(gè)SQL Thread,讀取本地的relay log,寫入到本地,從而實(shí)現(xiàn)數(shù)據(jù)同步。
數(shù)據(jù)同步方案
根據(jù)上面的mysql主從數(shù)據(jù)復(fù)制方案,那我們是不是可以自己寫個(gè)組件,也讀取binlog日志,解析出sql語句;然后同步到另一個(gè)mysql集群呢?
這樣就可以實(shí)現(xiàn)了一個(gè)集群的數(shù)據(jù),同步到另一個(gè)集群中。
那這個(gè)組件需要我們自己寫嗎?這個(gè)組件可以參考binlog的協(xié)議,只要有資深的網(wǎng)絡(luò)編程知識(shí),是能夠?qū)崿F(xiàn)的。
當(dāng)然現(xiàn)在也不需要我們自己編寫,現(xiàn)在市面上有成熟開源的
- 阿里巴巴開源的canal
- 美團(tuán)開源的puma
- linkedin開源的databus
我們可以利用這些開源組件訂閱binlog日志,解析到變化數(shù)據(jù)同步到目標(biāo)庫中。整個(gè)過程可以分為2步,第一步訂閱獲得變化的數(shù)據(jù),第二步是把變化數(shù)據(jù)更新到其他目標(biāo)庫。
這邊所說的目標(biāo)庫,不單單為mysql集群,也可以為redis,es等
上圖我們通過訂閱binlog,完成比較有代表性的數(shù)據(jù)同步
多機(jī)房Mysql同步
根據(jù)上面的知識(shí),多機(jī)房的mysql的數(shù)據(jù)同步,可以也采用binlog方案
北京用戶的數(shù)據(jù)不斷寫入離自己最近的機(jī)房的DB,通過binlog訂閱組件訂閱這個(gè)庫binlog,然后下游的更新組件將binlog轉(zhuǎn)換成SQL,插入到目標(biāo)庫。上海用戶類似,只不過方向相反,不再贅述。通過這種方式,我們可以實(shí)時(shí)的將兩個(gè)庫的數(shù)據(jù)同步到對端。
上面的方案面對binlog更新不頻繁的場景,應(yīng)該問題不大;但是如果更新很頻繁,那么binlog日志量會(huì)很大,處理更新數(shù)據(jù)的組件很有可能會(huì)頂不住,那如何處理?
優(yōu)化同步方案
為了解決binlog量過大,更新數(shù)據(jù)組件處理不過來,可以在此方案中加入MQ進(jìn)行削峰,如下圖
同步方案的問題
我們看到上面的架構(gòu),主要是針對增量數(shù)據(jù)的同步;但一開始項(xiàng)目上線的時(shí)候,全量數(shù)據(jù)怎么處理呢?這個(gè)一般的處理策略是DBA先dump一份源庫完整的數(shù)據(jù)快照;目標(biāo)庫導(dǎo)入快照即可。
下面我們看看增量數(shù)據(jù)同步,仔細(xì)的小伙伴們應(yīng)該會(huì)看到北京IDC和上海IDC之間的數(shù)據(jù)是雙向的,因?yàn)楸本┯脩舻臄?shù)據(jù)是更新到北京DB的,上海用戶的數(shù)據(jù)是更新到上海DB的,所以業(yè)務(wù)上面也是必須是雙向的。
整個(gè)數(shù)據(jù)同步的過程會(huì)出現(xiàn)幾個(gè)問題:
如何解決重復(fù)插入?
考慮以下情況下,源庫中的一條記錄沒有唯一索引。對于這個(gè)記錄的binlog,通過更新組件將binlog轉(zhuǎn)換成sql插入目標(biāo)庫時(shí),拋出了異常,此時(shí)我們并不知道知道是否插入成功了,則需要進(jìn)行重試。如果之前已經(jīng)是插入目標(biāo)庫成功,只是目標(biāo)庫響應(yīng)時(shí)網(wǎng)絡(luò)超時(shí)(socket timeout)了,導(dǎo)致的異常,這個(gè)時(shí)候重試插入,就會(huì)存在多條記錄,造成數(shù)據(jù)不一致。
因此,通常,在數(shù)據(jù)同步時(shí),通常會(huì)限制記錄必須有要有主鍵或者唯一索引。
對于DDL語句如何處理?
如果數(shù)據(jù)庫表中已經(jīng)有大量數(shù)據(jù),例如千萬級(jí)別、或者上億,這個(gè)時(shí)候?qū)τ谶@個(gè)表的DDL變更,將會(huì)變得非常慢,可能會(huì)需要幾分鐘甚至更長時(shí)間,而DDL操作是會(huì)鎖表的,這必然會(huì)對業(yè)務(wù)造成極大的影響。
因此,同步組件通常會(huì)對DDL語句進(jìn)行過濾,不進(jìn)行同步。DBA在不同的數(shù)據(jù)庫集群上,通過一些在線DDL工具進(jìn)行表結(jié)構(gòu)變更。
如何解決唯一索引沖突?
由于兩邊的庫都存在數(shù)據(jù)插入,如果都使用了同一個(gè)唯一索引,那么在同步到對端時(shí),將會(huì)產(chǎn)生唯一索引沖突。對于這種情況,通常建議是使用一個(gè)全局唯一的分布式ID生成器來生成唯一索引,保證不會(huì)產(chǎn)生沖突。
另外,如果真的產(chǎn)生沖突了,同步組件應(yīng)該將沖突的記錄保存下來,以便之后的問題排查。
如何解決數(shù)據(jù)回環(huán)問題?
此問題是數(shù)據(jù)同步經(jīng)常出現(xiàn)的,也是必須需要解決的。最重要的問題。我們針對INSERT、UPDATE、DELETE三個(gè)操作來分別進(jìn)行說明:
INSERT操作
假設(shè)在A庫插入數(shù)據(jù),A庫產(chǎn)生binlog,之后同步到B庫,B庫同樣也會(huì)產(chǎn)生binlog。由于是雙向同步,這條記錄,又會(huì)被重新同步回A庫。由于A庫本來就存在這條記錄了,產(chǎn)生沖突。
UPDATE操作
先考慮針對A庫某條記錄R只有一次更新的情況,將R更新成R1,之后R1這個(gè)binlog會(huì)被同步到B庫,B庫又將R1同步會(huì)A庫。對于這種情況下,A庫將不會(huì)產(chǎn)生binlog。因?yàn)锳庫記錄當(dāng)前是R1,B庫同步回來的還是R1,意味著值沒有變。
在一個(gè)更新操作并沒有改變某條記錄值的情況下,mysql是不會(huì)產(chǎn)生binlog,相當(dāng)于同步終止。下圖演示了當(dāng)更新的值沒有變時(shí),mysql實(shí)際上不會(huì)做任何操作:
上圖演示了,數(shù)據(jù)中原本有一條記錄(1,"tianshouzhi”),之后執(zhí)行一個(gè)update語句,將id=1的記錄的name值再次更新為”tianshouzhi”,意味著值并沒有變更。這個(gè)時(shí)候,我們看到mysql 返回的影響的記錄函數(shù)為0,也就是說,并不會(huì)產(chǎn)生的更新操作。
小伙伴們是不是以為,update操作不會(huì)有回環(huán)問題了;事實(shí)上并不是,我們看一些場景:
考慮A庫的記錄R被連續(xù)更新了2次,第一次更新成R1,第二次被更新成R2;這兩條記錄變更信息都被同步到B庫,B也產(chǎn)生了R1和R2。由于B的數(shù)據(jù)也在往A同步,B的R1會(huì)被先同步到A,而A現(xiàn)在的值是R2,由于值不一樣,將會(huì)被更新成R1,并產(chǎn)生新的binlog;此時(shí)B的R2再同步會(huì)A,發(fā)現(xiàn)A的值是R1,又更新成R2,也產(chǎn)生binlog。由于B同步回A的操作,讓A又產(chǎn)生了新的binlog,A又要同步到B,如此反復(fù),陷入無限循環(huán)中。
這個(gè)后果將會(huì)進(jìn)入死循環(huán)。
DELETE操作
同樣存在先后順序問題。例如先插入一條記錄,再刪除。B在A刪除后,又將插入的數(shù)據(jù)同步回A,接著再將A的刪除操作也同步回A,每次都會(huì)產(chǎn)生binlog,陷入無限回環(huán)。
總結(jié)
今天老顧介紹了基本的多機(jī)房同步mysql的方案,以及同步方案遇到的一些問題,以及一些解決方案;但還遺留了數(shù)據(jù)回環(huán)問題,老顧將在下一篇文章中介紹解決方案。謝謝!!!
---End---
總結(jié)
以上是生活随笔為你收集整理的canal mysql从库_大厂如何基于binlog解决多机房同步mysql数据(一)?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c语言搜索多层文件夹,c语言 遍历搜索文
- 下一篇: mysql出现core dumped_m