數(shù)據(jù)庫高可用架構(gòu)設(shè)計,看這篇就夠了

你好,我是小編沐子。

又趕上一年一度的金九銀十的日子,這段期間的招聘崗位相對前幾個月會多些,如果在目前公司沒有進(jìn)步、沒有前途時,這段時間可以準(zhǔn)備一下,去外面看看機(jī)會。不過在外面找工作時,可以提前在網(wǎng)上看看招聘信息,看看自己是否達(dá)到公司要求。如果多看下高薪資的技術(shù)人員招聘要求時,就會發(fā)現(xiàn)對三高都有一定的要求,比如下面一家公司的要求就對高并發(fā)、高負(fù)載和高可用性系統(tǒng)設(shè)計要有開發(fā)經(jīng)驗(yàn)。

現(xiàn)實(shí)是我們大部分的公司都很少會遇到三高的場景,即很少有這方面的設(shè)計開發(fā)經(jīng)驗(yàn),不過我們可以提前學(xué)習(xí)三高的方案,盡量把這些方案用在工作上,即使在工作中用不到,那么在面試中也會有好處或者對以后的工作也會起到一定的幫助。這篇文章我就來和大家聊一聊,我在工作中用到的數(shù)據(jù)庫高可用方案,以及采用這些方案所遇到的問題,希望這些心得可以幫助到你。


一、 高可用背景

高可用概念

高可用(High Availability)是系統(tǒng)所能提供無故障服務(wù)的一種能力。簡單地說就是避免因服務(wù)器宕機(jī)而造成的服務(wù)不可用。


通常來說,系統(tǒng)至少要達(dá)到 4 個 9(99.99%),也就是每年宕機(jī)時間不超過 52.56 分鐘,否則用戶體驗(yàn)會非常差,感覺系統(tǒng)不穩(wěn)定。

99.99% = 1 - 52.56 / (365*24*60)

不過

4 個 9 宕機(jī) 52 分鐘對于生產(chǎn)環(huán)境的影響還是比較大,但是 5 個 9 對大部分系統(tǒng)來說要求又太高。所以一些云服務(wù)商會提出一個

99.995% 的可用性概念,那么系統(tǒng)一年的不可用時長為:不可用時長 = (1 - 99.995%)*365*24*60 = 26.28

(分鐘),即一年最多的影響服務(wù)的時間為 26.28 分鐘。

簡單了解“高可用”有多么重要之后,接下來我們就來看一下,怎么設(shè)計數(shù)據(jù)庫高可用架構(gòu)。系統(tǒng)要達(dá)到高可用,一定要做好軟硬件的冗余,消除單點(diǎn)故障(SPOF single point of failure)。冗余是高可用的基礎(chǔ),通常認(rèn)為,系統(tǒng)投入硬件資源越多,冗余也就越多,系統(tǒng)可用性也就越高。除了做好冗余,系統(tǒng)還要做好故障轉(zhuǎn)移(Failover)的處理。也就是在最短的時間內(nèi)發(fā)現(xiàn)故障,然后把業(yè)務(wù)切換到冗余的資源上。在介紹高可用架構(gòu)之前,我們先了解一下數(shù)據(jù)庫復(fù)制的原理

二、 數(shù)據(jù)庫復(fù)制原理

數(shù)據(jù)庫復(fù)制本質(zhì)上就是數(shù)據(jù)同步。MySQL 數(shù)據(jù)庫是基于二進(jìn)制日志(binary log)進(jìn)行數(shù)據(jù)增量同步,而二進(jìn)制日志記錄了所有對于 MySQL 數(shù)據(jù)庫的修改操作。

在默認(rèn) ROW 格式二進(jìn)制日志中,一條 SQL 操作影響的記錄會被全部記錄下來,比如一條 SQL語句更新了三行記錄,在二進(jìn)制日志中會記錄被修改的這三條記錄的前項(xiàng)(before image)和后項(xiàng)(after image)。

在有二進(jìn)制日志的基礎(chǔ)上,MySQL 數(shù)據(jù)庫就可以通過數(shù)據(jù)復(fù)制技術(shù)實(shí)現(xiàn)數(shù)據(jù)同步了。而數(shù)據(jù)復(fù)制的本質(zhì)就是把一臺 MySQL 數(shù)據(jù)庫上的變更同步到另一臺 MySQL 數(shù)據(jù)庫上,下面這張圖顯示了當(dāng)前 MySQL 數(shù)據(jù)庫的復(fù)制架構(gòu):


可以看到,在 MySQL 復(fù)制中,一臺是數(shù)據(jù)庫的角色是 Master(也叫 Primary),剩下的服務(wù)器角色是 Slave(也叫 Standby):

a. Master 服務(wù)器會把數(shù)據(jù)變更產(chǎn)生的二進(jìn)制日志通過 Dump 線程發(fā)送給 Slave 服務(wù)器;

b. Slave 服務(wù)器中的 I/O 線程負(fù)責(zé)接受二進(jìn)制日志,并保存為中繼日志;

c. SQL/Worker 線程負(fù)責(zé)并行執(zhí)行中繼日志,即在 Slave 服務(wù)器上回放 Master 產(chǎn)生的日志。

得益于二進(jìn)制日志,MySQL 的復(fù)制相比其他數(shù)據(jù)庫,如 Oracle、PostgreSQL 等,非常靈活,用戶可以根據(jù)自己的需要構(gòu)建所需要的復(fù)制拓?fù)浣Y(jié)構(gòu),比如:


在上圖中,Slave1、Slave2、Slave3

都是 Master 的從服務(wù)器,而 Slave11 是 Slave1 的從服務(wù)器,Slave1 服務(wù)器既是 Master 的從機(jī),又是

Slave11 的主機(jī),所以 Slave1 是個級聯(lián)的從機(jī)。同理,Slave3 也是臺級聯(lián)的從機(jī)。

MySQL復(fù)制類型及應(yīng)用選項(xiàng)

MySQL 復(fù)制可以分為以下幾種類型:

默認(rèn)的復(fù)制是異步復(fù)制,而很多新同學(xué)因?yàn)椴涣私?MySQL 除了異步復(fù)制還有其他復(fù)制的類型,所以錯誤地在業(yè)務(wù)中使用了異步復(fù)制。為了解決這個問題,我們一起詳細(xì)了解一下每種復(fù)制類型,以及它們在業(yè)務(wù)中的選型,方便你在業(yè)務(wù)做正確的選型。

異步復(fù)制

在異步復(fù)制(async

replication)中,Master 不用關(guān)心 Slave 是否接收到二進(jìn)制日志,所以 Master 與 Slave

沒有任何的依賴關(guān)系。你可以認(rèn)為 Master 和 Slave 是分別獨(dú)自工作的兩臺服務(wù)器,數(shù)據(jù)最終會通過二進(jìn)制日志達(dá)到一致。

異步復(fù)制的性能最好,因?yàn)樗鼘?shù)據(jù)庫本身幾乎沒有任何開銷,除非主從延遲非常大,Dump Thread 需要讀取大量二進(jìn)制日志文件。

如果業(yè)務(wù)對于數(shù)據(jù)一致性要求不高,當(dāng)發(fā)生故障時,能容忍數(shù)據(jù)的丟失,甚至大量的丟失,推薦用異步復(fù)制,這樣性能最好(比如像微博這樣的業(yè)務(wù),雖然它對性能的要求極高,但對于數(shù)據(jù)丟失,通??梢匀萑蹋5诵臉I(yè)務(wù)系統(tǒng)最關(guān)心的就是數(shù)據(jù)安全,比如監(jiān)控業(yè)務(wù)、告警系統(tǒng)。

半同步復(fù)制

半同步復(fù)制要求 Master 事務(wù)提交過程中,至少有 N 個 Slave 接收到二進(jìn)制日志,這樣就能保證當(dāng) Master 發(fā)生宕機(jī),至少有 N 臺 Slave 服務(wù)器中的數(shù)據(jù)是完整的。

半同步復(fù)制并不是 MySQL 內(nèi)置的功能,而是要安裝半同步插件,并啟用半同步復(fù)制功能,設(shè)置 N 個 Slave 接受二進(jìn)制日志成功,比如:

plugin-load="rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisync_slave.so"

rpl-semi-sync-master-enabled = 1

rpl-semi-sync-slave-enabled = 1

rpl_semi_sync_master_wait_no_slave = 1

上面的配置中:

第 1 行要求數(shù)據(jù)庫啟動時安裝半同步插件;

第 2、3 行表示分別啟用半同步 Master 和半同步 Slave 插件;

第 4 行表示半同步復(fù)制過程中,提交的事務(wù)必須至少有一個 Slave 接收到二進(jìn)制日志。

在半同步復(fù)制中,有損半同步復(fù)制是 MySQL 5.7 版本前的半同步復(fù)制機(jī)制,這種半同步復(fù)制在Master 發(fā)生宕機(jī)時,Slave 會丟失最后一批提交的數(shù)據(jù),若這時 Slave 提升(Failover)為Master,可能會發(fā)生已經(jīng)提交的事情不見了,發(fā)生了回滾的情況。

有損半同步復(fù)制原理如下圖所示:


可以看到,有損半同步是在

Master 事務(wù)提交后,即步驟 4 后,等待 Slave 返回 ACK,表示至少有 Slave

接收到了二進(jìn)制日志,如果這時二進(jìn)制日志還未發(fā)送到 Slave,Master 就發(fā)生宕機(jī),則此時 Slave 就會丟失 Master

已經(jīng)提交的數(shù)據(jù)。

而 MySQL 5.7 的無損半同步復(fù)制解決了這個問題,其原理如下圖所示:

從上圖可以看到,無損半同步復(fù)制 WAIT ACK 發(fā)生在事務(wù)提交之前,這樣即便 Slave 沒有收到二進(jìn)制日志,但是 Master 宕機(jī)了,由于最后一個事務(wù)還沒有提交,所以本身這個數(shù)據(jù)對外也不可見,不存在丟失的問題。

所以,對于任何有數(shù)據(jù)一致性要求的業(yè)務(wù),如電商的核心訂單業(yè)務(wù)、銀行、保險、證券等與資金密切相關(guān)的業(yè)務(wù),務(wù)必使用無損半同步復(fù)制。這樣數(shù)據(jù)才是安全的、有保障的、即使發(fā)生宕機(jī),從機(jī)也有一份完整的數(shù)據(jù)。

多源復(fù)制

無論是異步復(fù)制還是半同步復(fù)制,都是 1 個 Master 對應(yīng) N 個 Slave。其實(shí) MySQL 也支持 N 個 Master 對應(yīng) 1 個 Slave,這種架構(gòu)就稱之為多源復(fù)制。

多源復(fù)制允許在不同 MySQL 實(shí)例上的數(shù)據(jù)同步到 1 臺 MySQL 實(shí)例上,方便在 1 臺 Slave 服務(wù)器上進(jìn)行一些統(tǒng)計查詢,如常見的 OLAP 業(yè)務(wù)查詢。

多源復(fù)制的架構(gòu)如下所示:

上圖顯示了訂單庫、庫存庫、供應(yīng)商庫,通過多源復(fù)制同步到了一臺 MySQL 實(shí)例上,接著就可以通過 MySQL 8.0 提供的復(fù)雜 SQL 能力,對業(yè)務(wù)進(jìn)行深度的數(shù)據(jù)分析和挖掘。

延遲復(fù)制

前面介紹的復(fù)制架構(gòu),Slave 在接收二進(jìn)制日志后會盡可能快地回放日志,這樣是為了避免主從之間出現(xiàn)延遲。而延遲復(fù)制卻允許Slave 延遲回放接收到的二進(jìn)制日志,為了避免主服務(wù)器上的誤操作,馬上又同步到了從服務(wù)器,導(dǎo)致數(shù)據(jù)完全丟失。

我們可以通過以下命令設(shè)置延遲復(fù)制:

CHANGE MASTER TO master_delay = 3600

這樣就人為設(shè)置了 Slave 落后 Master 服務(wù)器1個小時。

延遲復(fù)制主要用于誤操作防范,也在數(shù)據(jù)庫的備份架構(gòu)設(shè)計中非常常見,比如可以設(shè)置一個延遲一天的延遲備機(jī),這樣本質(zhì)上說,用戶可以有 1 份 24 小時前的快照。

那么當(dāng)線上發(fā)生誤操作,如 DROP TABLE、DROP DATABASE 這樣災(zāi)難性的命令時,用戶有一個 24 小時前的快照,數(shù)據(jù)可以快速恢復(fù)

對金融行業(yè)來說,延遲復(fù)制是你備份設(shè)計中,必須考慮的一個架構(gòu)部分。

在我們了解了數(shù)據(jù)同步的原理后,接下來我們進(jìn)入常見的數(shù)據(jù)庫高可用架構(gòu)方案。

三、 常見的架構(gòu)方案

1. 常見的架構(gòu)方案

方案一:主備架構(gòu),只有主庫提供讀寫服務(wù),備庫冗余作故障轉(zhuǎn)移用

1、高可用分析:高可用,主庫掛了,keepalive(只是一種工具)會自動切換到備庫。這個過程對業(yè)務(wù)層是透明的,無需修改代碼或配置。

2、高性能分析:讀寫都操作主庫,很容易產(chǎn)生瓶頸。大部分互聯(lián)網(wǎng)應(yīng)用讀多寫少,讀會先成為瓶頸,進(jìn)而影響寫性能。另外,備庫只是單純的備份,資源利用率50%,這點(diǎn)方案二可解決。

3、一致性分析:讀寫都操作主庫,不存在數(shù)據(jù)一致性問題。

4、擴(kuò)展性分析:無法通過加從庫來擴(kuò)展讀性能,進(jìn)而提高整體性能。

5、可落地分析:兩點(diǎn)影響落地使用。第一,性能一般,這點(diǎn)可以通過建立高效的索引和引入緩存來增加讀性能,進(jìn)而提高性能。這也是通用的方案。第二,擴(kuò)展性差,這點(diǎn)可以通過分庫分表來擴(kuò)展。

方案二:雙主架構(gòu),兩個主庫同時提供服務(wù),負(fù)載均衡

1、高可用分析:高可用,一個主庫掛了,不影響另一臺主庫提供服務(wù)。這個過程對業(yè)務(wù)層是透明的,無需修改代碼或配置。

2、高性能分析:讀寫性能相比于方案一都得到提升,提升一倍。

3、一致性分析:存在數(shù)據(jù)一致性問題。請看,一致性解決方案。

4、擴(kuò)展性分析:當(dāng)然可以擴(kuò)展成三主循環(huán),但筆者不建議(會多一層數(shù)據(jù)同步,這樣同步的時間會更長)。如果非得在數(shù)據(jù)庫架構(gòu)層面擴(kuò)展的話,擴(kuò)展為方案四。

5、可落地分析:兩點(diǎn)影響落地使用。第一,數(shù)據(jù)一致性問題,一致性解決方案可解決問題。第二,主鍵沖突問題,ID統(tǒng)一地由分布式ID生成服務(wù)來生成可解決問題。

方案三:主從架構(gòu),一主多從,讀寫分離

1、高可用分析:主庫單點(diǎn),從庫高可用。一旦主庫掛了,寫服務(wù)也就無法提供。

2、高性能分析:大部分互聯(lián)網(wǎng)應(yīng)用讀多寫少,讀會先成為瓶頸,進(jìn)而影響整體性能。讀的性能提高了,整體性能也提高了。另外,主庫可以不用索引,線上從庫和線下從庫也可以建立不同的索引(線上從庫如果有多個還是要建立相同的索引,不然得不償失;線下從庫是平時開發(fā)人員排查線上問題時查的庫,可以建更多的索引)。

3、一致性分析:存在數(shù)據(jù)一致性問題。請看,一致性解決方案。

4、擴(kuò)展性分析:可以通過加從庫來擴(kuò)展讀性能,進(jìn)而提高整體性能。(帶來的問題是,從庫越多需要從主庫拉取binlog日志的端就越多,進(jìn)而影響主庫的性能,并且數(shù)據(jù)同步完成的時間也會更長)

5、可落地分析:兩點(diǎn)影響落地使用。第一,數(shù)據(jù)一致性問題,一致性解決方案可解決問題。第二,主庫單點(diǎn)問題,筆者暫時沒想到很好的解決方案。

注:思考一個問題,一臺從庫掛了會怎樣?讀寫分離之讀的負(fù)載均衡策略怎么容錯?

方案四:雙主+主從架構(gòu),看似完美的方案

1、高可用分析:高可用。

2、高性能分析:高性能。

3、一致性分析:存在數(shù)據(jù)一致性問題。請看,一致性解決方案 。

4、擴(kuò)展性分析:可以通過加從庫來擴(kuò)展讀性能,進(jìn)而提高整體性能。(帶來的問題同方案二

5、可落地分析:同方案二,但數(shù)據(jù)同步又多了一層,數(shù)據(jù)延遲更嚴(yán)重。

2. 一致性問題解決方案

第一類:主庫和從庫一致性解決方案

注:圖中圈出的是數(shù)據(jù)同步的地方,數(shù)據(jù)同步(從庫從主庫拉取binlog日志,再執(zhí)行一遍)是需要時間的,這個同步時間內(nèi)主庫和從庫的數(shù)據(jù)會存在不一致的情況。如果同步過程中有讀請求,那么讀到的就是從庫中的老數(shù)據(jù)。如下圖。

既然知道了數(shù)據(jù)不一致性產(chǎn)生的原因,有下面幾個解決方案供參考:

1、直接忽略,如果業(yè)務(wù)允許延時存在,那么就不去管它。

2、強(qiáng)制讀主,采用主備架構(gòu)方案,讀寫都走主庫。用緩存來擴(kuò)展數(shù)據(jù)庫讀性能 。

有一點(diǎn)需要知道:如果緩存掛了,可能會產(chǎn)生雪崩現(xiàn)象,不過一般分布式緩存都是高可用的。

3、選擇讀主,寫操作時根據(jù)庫+表+業(yè)務(wù)特征生成一個key放到Cache里并設(shè)置超時時間(大于等于主從數(shù)據(jù)同步時間)。讀請求時,同樣的方式生成key先去查Cache,再判斷是否命中。若命中,則讀主庫,否則讀從庫。代價是多了一次緩存讀寫,基本可以忽略。

4、半同步復(fù)制,等主從同步完成,寫請求才返回。就是大家常說的“半同步復(fù)制”semi-sync。這可以利用數(shù)據(jù)庫原生功能,實(shí)現(xiàn)比較簡單。代價是寫請求時延增長,吞吐量降低。

5、數(shù)據(jù)庫中間件,引入開源(mycat等)或自研的數(shù)據(jù)庫中間層。個人理解,思路同選擇讀主。數(shù)據(jù)庫中間件的成本比較高,并且還多引入了一層。

第二類:DB和緩存一致性解決方案

先來看一下常用的緩存使用方式:

第一步:淘汰緩存;

第二步:寫入數(shù)據(jù)庫;

第三步:讀取緩存?返回:讀取數(shù)據(jù)庫;

第四步:讀取數(shù)據(jù)庫后寫入緩存。

注:如果按照這種方式,圖一,不會產(chǎn)生DB和緩存不一致問題;圖二,會產(chǎn)生DB和緩存不一致問題,即r2.read先于w3.sync執(zhí)行。如果不做處理,緩存里的數(shù)據(jù)可能一直是臟數(shù)據(jù)。解決方式如下:

3. 總結(jié)

1、架構(gòu)演變

1、架構(gòu)演變一:方案一 -> 方案一+分庫分表 -> 方案二+分庫分表 -> 方案四+分庫分表;

2、架構(gòu)演變二:方案一 -> 方案一+分庫分表 -> 方案三+分庫分表 -> 方案四+分庫分表;

3、架構(gòu)演變?nèi)悍桨敢?-> 方案二 -> 方案四 -> 方案四+分庫分表;

4、架構(gòu)演變四:方案一 -> 方案三 -> 方案四 -> 方案四+分庫分表;

2、個人見解

1、加緩存和索引是通用的提升數(shù)據(jù)庫性能的方式;

2、分庫分表帶來的好處是巨大的,但同樣也會帶來一些問題。

3、不管是主備+分庫分表還是主從+讀寫分離+分庫分表,都要考慮具體的業(yè)務(wù)場景。58到家發(fā)展四年,絕大部分的數(shù)據(jù)庫架構(gòu)還是采用方案一和方案一+分庫分表,只有極少部分用方案三+讀寫分離+分庫分表。另外,阿里云提供的數(shù)據(jù)庫云服務(wù)也都是主備方案,要想主從+讀寫分離需要二次架構(gòu)。

4、記住一句話:不考慮業(yè)務(wù)場景的架構(gòu)都是耍流氓。

四、 容災(zāi)方案

高可用用于處理各種宕機(jī)問題,而宕機(jī)可以分成服務(wù)器宕機(jī)、機(jī)房級宕機(jī),甚至是一個城市發(fā)生宕機(jī)。

機(jī)房級宕機(jī):機(jī)房光纖不通/被挖斷,機(jī)房整體掉電(雙路備用電源也不可用);

城市級宕機(jī):一般指整個城市的進(jìn)出口網(wǎng)絡(luò),骨干交換機(jī)發(fā)生的故障(這種情況發(fā)生的概率很小)。

如果綜合考慮的話,高可用就成了一種容災(zāi)處理機(jī)制,對應(yīng)的高可用架構(gòu)的評判標(biāo)準(zhǔn)就上升了。

機(jī)房內(nèi)容災(zāi):機(jī)房內(nèi)某臺數(shù)據(jù)庫服務(wù)器不可用,切換到同機(jī)房的數(shù)據(jù)庫實(shí)例,保障業(yè)務(wù)連續(xù)性;

同城容災(zāi):機(jī)房不可用,切換到同城機(jī)房的數(shù)據(jù)庫實(shí)例,保障業(yè)務(wù)連續(xù)性;

跨城容災(zāi):單個城市機(jī)房都不可用,切換到跨城機(jī)房的數(shù)據(jù)庫實(shí)例,保障業(yè)務(wù)連續(xù)性。

前面我們談到的高可用設(shè)計,都只是機(jī)房內(nèi)的容災(zāi)。也就是說,我們的主服務(wù)器和從服務(wù)器都在一個機(jī)房內(nèi),現(xiàn)在我們來看一下同城和跨城的容災(zāi)設(shè)計(我提醒一下,不論是機(jī)房內(nèi)容災(zāi)、同城容災(zāi),還是跨城容災(zāi),都是基于 MySQL 的無損半同步復(fù)制,只是物理部署方式不同,解決不同的問題)。

對于同城容災(zāi),我看到很多這樣的設(shè)計:

這種設(shè)計沒有考慮到機(jī)房網(wǎng)絡(luò)的抖動。如果機(jī)房 1 和機(jī)房 2 之間的網(wǎng)絡(luò)發(fā)生抖動,那么因?yàn)槭聞?wù)提交需要機(jī)房 2 中的從服務(wù)器接收日志,所以會出現(xiàn)事務(wù)提交被 hang 住的問題。

而機(jī)房網(wǎng)絡(luò)抖動非常常見,所以核心業(yè)務(wù)同城容災(zāi)務(wù)要采用三園區(qū)的架構(gòu),如下圖所示:

該架構(gòu)稱為“三園區(qū)的架構(gòu)”,如果三個機(jī)房都在一個城市,則稱為“ 一地三中心”,如果在相鄰兩個城市,那么就叫“兩地三中心”。但這種同城/近城容災(zāi),要求機(jī)房網(wǎng)絡(luò)之間的延遲不超過 5ms。

在三園區(qū)架構(gòu)中,一份數(shù)據(jù)被存放在了 3 個機(jī)房,機(jī)房之間根據(jù)半同步復(fù)制。這里將 MySQL 的半同步復(fù)制參數(shù)

rpl_semi_sync_master_wait_for_slave_count 設(shè)置為 1,表示只要有 1 個半同步備機(jī)接收到日志,主服務(wù)器上的事務(wù)就可以提交。

這樣的設(shè)計,保證除主機(jī)房外,數(shù)據(jù)在其他機(jī)房至少一份完整的數(shù)據(jù)。

另外,即便機(jī)房 1 與機(jī)房 2 發(fā)生網(wǎng)絡(luò)抖動,因?yàn)闄C(jī)房 1 與機(jī)房 3 之間的網(wǎng)絡(luò)很好,不會影響事務(wù)在主服務(wù)器上的提交。如果機(jī)房 1 的出口交換機(jī)或光纖發(fā)生故障,那么這時高可用套件會 FAILOVER 到機(jī)房 2 或機(jī)房 3,因?yàn)橹辽儆幸环輸?shù)據(jù)是完整的。

機(jī)房 2、機(jī)房 3 的數(shù)據(jù)用于保障數(shù)據(jù)一致性,但是如果要實(shí)現(xiàn)讀寫分離,或備份,還需要引入異步復(fù)制的備機(jī)節(jié)點(diǎn)。所以整體架構(gòu)調(diào)整為:

從圖中可以看到,我們加入兩個異步復(fù)制的節(jié)點(diǎn),用于業(yè)務(wù)實(shí)現(xiàn)讀寫分離,另外再從機(jī)房 3 的備機(jī)中,引入一個異步復(fù)制的延遲備機(jī),用于做數(shù)據(jù)誤刪除操作的恢復(fù)。

當(dāng)設(shè)計成類似上述的架構(gòu)時,你才能認(rèn)為自己的同城容災(zāi)架構(gòu)是合格的!

另一個重要的點(diǎn):因?yàn)闄C(jī)房 1 中的主服務(wù)器要向四個從服務(wù)器發(fā)送日志,這時網(wǎng)卡有成為瓶頸的可能,所以請務(wù)必配置萬兆網(wǎng)卡。

在明白三園區(qū)架構(gòu)后,要實(shí)現(xiàn)跨城容災(zāi)也就非常簡單了, 只要把三個機(jī)房放在不同城市就行。但這樣的設(shè)計,當(dāng)主服務(wù)器發(fā)生宕機(jī)時,數(shù)據(jù)庫就會切到跨城,而跨城之間的網(wǎng)絡(luò)延遲超過了25 ms。所以,跨城容災(zāi)一般設(shè)計成“三地五中心”的架構(gòu),如下圖所示:

由于有五個機(jī)房,所以 ACK 設(shè)置為 2,保證至少一份數(shù)據(jù)在兩個機(jī)房有數(shù)據(jù)。這樣當(dāng)發(fā)生城市級故障,則城市 2 或城市 3 中,至少有一份完整的數(shù)據(jù)。

在真實(shí)的互聯(lián)網(wǎng)業(yè)務(wù)場景中,“三地五中心”應(yīng)用并不像“三園區(qū)”那樣普遍。這是因?yàn)?25ms的延遲對業(yè)務(wù)的影響非常大,一般這種架構(gòu)應(yīng)用于讀多寫少的場景,比如用戶中心。

另外,真實(shí)的互聯(lián)網(wǎng)業(yè)務(wù)場景中,實(shí)現(xiàn)跨城容災(zāi),一般基于同城容災(zāi)架構(gòu),然后再由業(yè)務(wù)層來保障跨城的數(shù)據(jù)一致性。

五、 兜底策略:數(shù)據(jù)核對

到目前為止,我們的高可用是基于 MySQL 的復(fù)制技術(shù)。但你有沒有想過這樣幾個問題:

萬一數(shù)據(jù)庫的復(fù)制有 Bug 呢?導(dǎo)致最終的數(shù)據(jù)在邏輯上不一致呢?主從的數(shù)據(jù)一定一致嗎?你如何判斷一定一致呢?

所以,除了高可用的容災(zāi)架構(gòu)設(shè)計,我們還要做一層兜底服務(wù),用于判斷數(shù)據(jù)的一致性。這里要引入數(shù)據(jù)核對,用來解決以下兩方面的問題。

數(shù)據(jù)在業(yè)務(wù)邏輯上一致:這個保障業(yè)務(wù)是對的;

主從服務(wù)器之間的數(shù)據(jù)一致:這個保障從服務(wù)器的數(shù)據(jù)是安全的、可切的。

業(yè)務(wù)邏輯核對由業(yè)務(wù)的同學(xué)負(fù)責(zé)編寫, 從整個業(yè)務(wù)邏輯調(diào)度看賬平不平。例如“今天庫存的消耗”是否等于“訂單明細(xì)表中的總和”,“在途快遞” + “已收快遞”是否等于“已下快遞總和”。總之,這是個業(yè)務(wù)邏輯,用于對賬。

主從服務(wù)器之間的核對,是由數(shù)據(jù)庫團(tuán)隊負(fù)責(zé)的。需要額外寫一個主從核對服務(wù),用于保障主從數(shù)據(jù)的一致性。這個核對不依賴復(fù)制本身,也是一種邏輯核對。思路是:將最近一段時間內(nèi)主服務(wù)器上變更過的記錄與從服務(wù)器核對,從邏輯上驗(yàn)證是否一致。其實(shí)現(xiàn)如圖所示:

那么現(xiàn)在的難題是:如何判斷最近一段時間內(nèi)主服務(wù)器上變更過的記錄?這里有兩種思路:

1) 表結(jié)構(gòu)設(shè)計規(guī)范中,有講過每張表有一個 last_modify_date,用于記錄每條記錄的最后修改時間,按照這個條件過濾就能查出最近更新的記錄,然后每條記錄比較即可。

2) 核對服務(wù)掃描最近的二進(jìn)制日志,篩選出最近更新過記錄的表和主鍵,然后核對數(shù)據(jù)。這種的實(shí)現(xiàn)難度會更大一些,但是不要求在數(shù)據(jù)庫上進(jìn)行查詢。

如果在核對過程中,記錄又在主上發(fā)生了變化,但是還沒有同步到從機(jī),我們可以加入復(fù)核邏輯,按理來說多復(fù)核幾次,主從數(shù)據(jù)應(yīng)該就一致了。如果復(fù)核多次不一致,那么大概率,主從數(shù)據(jù)就已經(jīng)是不一致的了。

核對服務(wù)的邏輯比較簡單,但是要實(shí)現(xiàn)線上業(yè)務(wù)的數(shù)據(jù)核對,開發(fā)上還是有一些挑戰(zhàn),但這不就是我們 DBA 的價值所在嗎?

總結(jié)

小編沐子總結(jié)了數(shù)據(jù)庫高可用的架構(gòu)設(shè)計,內(nèi)容非常干貨,建議你反復(fù)閱讀,其中涉及的內(nèi)容在原理上并不復(fù)雜,但在實(shí)現(xiàn)細(xì)節(jié)上需要不斷打磨,歡迎你在后續(xù)的架構(gòu)設(shè)計過程中與我交流,總結(jié)來說:

1. 核心業(yè)務(wù)復(fù)制務(wù)必為無損半同步復(fù)制;

2. 同城容災(zāi)使用三園區(qū)架構(gòu),一地三中心,或者兩地三中心,機(jī)房見網(wǎng)絡(luò)延遲不超過5ms;

3. 跨城容災(zāi)使用"三地五中心",跨城機(jī)房距離超過200KM,延遲超過25ms;

4. 跨城容災(zāi)架構(gòu)由于網(wǎng)絡(luò)耗時高,因此一般僅用于讀多寫少的業(yè)務(wù),例如用戶中心;

5. 除了復(fù)制進(jìn)行數(shù)據(jù)同步外,還需要額外的核對程序進(jìn)行邏輯核對;

6. 數(shù)據(jù)庫層的邏輯核對,可以使用last_modify_date字段,取出最近修改的記錄。

最后,小編提出一個思考題:對于跨城容災(zāi),有什么優(yōu)化技術(shù)可以減少耗時增大帶來的性能影響呢?歡迎大家留言與小編交流,一同探討和學(xué)習(xí)。歡迎關(guān)注公眾號(微信搜索公眾號:首席架構(gòu)師專欄),里面有許多技術(shù)干貨,也有我對技術(shù)的思考和感悟,還有作為架構(gòu)師的驗(yàn)驗(yàn)分享;關(guān)注后回復(fù) 【面試題】,有我準(zhǔn)備的面試題、架構(gòu)師大型項(xiàng)目實(shí)戰(zhàn)視頻等福利 , 小編會帶著你一起學(xué)習(xí)、成長,讓我們一起加油?。?!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容