支付宝架构真牛逼
自 2008 年雙 11 以來(lái),在每年雙 11 超大規(guī)模流量的沖擊上,螞蟻金服都會(huì)不斷突破現(xiàn)有技術(shù)的極限。
2010 年雙 11 的支付峰值為 2 萬(wàn)筆/分鐘,到 2017 年雙 11 時(shí)這個(gè)數(shù)字變?yōu)榱?25.6 萬(wàn)筆/秒。
2018 年雙 11 的支付峰值為 48 萬(wàn)筆/秒,2019 年雙 11 支付峰值為 54.4 萬(wàn)筆/秒,創(chuàng)下新紀(jì)錄,是 2009 年第一次雙 11 的 1360?倍。
在如此之大的支付 TPS 背后除了削峰等錦上添花的應(yīng)用級(jí)優(yōu)化,最解渴最實(shí)質(zhì)的招數(shù)當(dāng)數(shù)基于分庫(kù)分表的單元化了,螞蟻技術(shù)稱之為 LDC(邏輯數(shù)據(jù)中心)。
本文不打算討論具體到代碼級(jí)的分析,而是嘗試用最簡(jiǎn)單的描述來(lái)說(shuō)明其中最大快人心的原理。
我想關(guān)心分布式系統(tǒng)設(shè)計(jì)的人都曾被下面這些問(wèn)題所困擾過(guò):
支付寶海量支付背后最解渴的設(shè)計(jì)是啥?換句話說(shuō),實(shí)現(xiàn)支付寶高 TPS 的最關(guān)鍵的設(shè)計(jì)是啥?
LDC 是啥?LDC 怎么實(shí)現(xiàn)異地多活和異地災(zāi)備的?
CAP 魔咒到底是啥?P 到底怎么理解?
什么是腦裂?跟 CAP 又是啥關(guān)系?
什么是 PAXOS,它解決了啥問(wèn)題?
PAXOS 和 CAP 啥關(guān)系?PAXOS 可以逃脫 CAP 魔咒么?
Oceanbase 能逃脫 CAP 魔咒么?
如果你對(duì)這些感興趣,不妨看一場(chǎng)赤裸裸的論述,拒絕使用晦澀難懂的詞匯,直面最本質(zhì)的邏輯。
LDC?和單元化
LDC(logic data center)是相對(duì)于傳統(tǒng)的(Internet Data Center-IDC)提出的,邏輯數(shù)據(jù)中心所表達(dá)的中心思想是無(wú)論物理結(jié)構(gòu)如何的分布,整個(gè)數(shù)據(jù)中心在邏輯上是協(xié)同和統(tǒng)一的。
這句話暗含的是強(qiáng)大的體系設(shè)計(jì),分布式系統(tǒng)的挑戰(zhàn)就在于整體協(xié)同工作(可用性,分區(qū)容忍性)和統(tǒng)一(一致性)。
單元化是大型互聯(lián)網(wǎng)系統(tǒng)的必然選擇趨勢(shì),舉個(gè)最最通俗的例子來(lái)說(shuō)明單元化。
我們總是說(shuō) TPS 很難提升,確實(shí)任何一家互聯(lián)網(wǎng)公司(比如淘寶、攜程、新浪)它的交易 TPS 頂多以十萬(wàn)計(jì)量(平均水平),很難往上串了。
因?yàn)閿?shù)據(jù)庫(kù)存儲(chǔ)層瓶頸的存在再多水平擴(kuò)展的服務(wù)器都無(wú)法繞開(kāi),而從整個(gè)互聯(lián)網(wǎng)的視角看,全世界電商的交易 TPS 可以輕松上億。
這個(gè)例子帶給我們一些思考:為啥幾家互聯(lián)網(wǎng)公司的 TPS 之和可以那么大,服務(wù)的用戶數(shù)規(guī)模也極為嚇人,而單個(gè)互聯(lián)網(wǎng)公司的 TPS 卻很難提升?
究其本質(zhì),每家互聯(lián)網(wǎng)公司都是一個(gè)獨(dú)立的大型單元,他們各自服務(wù)自己的用戶互不干擾。
這就是單元化的基本特性,任何一家互聯(lián)網(wǎng)公司,其想要成倍的擴(kuò)大自己系統(tǒng)的服務(wù)能力,都必然會(huì)走向單元化之路。
它的本質(zhì)是分治,我們把廣大的用戶分為若干部分,同時(shí)把系統(tǒng)復(fù)制多份,每一份都獨(dú)立部署,每一份系統(tǒng)都服務(wù)特定的一群用戶。
以淘寶舉例,這樣之后,就會(huì)有很多個(gè)淘寶系統(tǒng)分別為不同的用戶服務(wù),每個(gè)淘寶系統(tǒng)都做到十萬(wàn) TPS 的話,N 個(gè)這樣的系統(tǒng)就可以輕松做到 N*十萬(wàn)的 TPS 了。
LDC 實(shí)現(xiàn)的關(guān)鍵就在于單元化系統(tǒng)架構(gòu)設(shè)計(jì),所以在螞蟻內(nèi)部,LDC 和單元化是不分家的,這也是很多同學(xué)比較困擾的地方,看似沒(méi)啥關(guān)系,實(shí)則是單元化體系設(shè)計(jì)成就了 LDC。
小結(jié):分庫(kù)分表解決的最大痛點(diǎn)是數(shù)據(jù)庫(kù)單點(diǎn)瓶頸,這個(gè)瓶頸的產(chǎn)生是由現(xiàn)代二進(jìn)制數(shù)據(jù)存儲(chǔ)體系決定的(即 I/O 速度)。
單元化只是分庫(kù)分表后系統(tǒng)部署的一種方式,這種部署模式在災(zāi)備方面也發(fā)揮了極大的優(yōu)勢(shì)。
系統(tǒng)架構(gòu)演化史
幾乎任何規(guī)模的互聯(lián)網(wǎng)公司,都有自己的系統(tǒng)架構(gòu)迭代和更新,大致的演化路徑都大同小異。
最早一般為了業(yè)務(wù)快速上線,所有功能都會(huì)放到一個(gè)應(yīng)用里,系統(tǒng)架構(gòu)如下圖所示:
這樣的架構(gòu)顯然是有問(wèn)題的,單機(jī)有著明顯的單點(diǎn)效應(yīng),單機(jī)的容量和性能都是很局限的,而使用中小型機(jī)會(huì)帶來(lái)大量的浪費(fèi)。
隨著業(yè)務(wù)發(fā)展,這個(gè)矛盾逐漸轉(zhuǎn)變?yōu)橹饕?#xff0c;因此工程師們采用了以下架構(gòu):
這是整個(gè)公司第一次觸碰到分布式,也就是對(duì)某個(gè)應(yīng)用進(jìn)行了水平擴(kuò)容,它將多個(gè)微機(jī)的計(jì)算能力團(tuán)結(jié)了起來(lái),可以完勝同等價(jià)格的中小型機(jī)器。
慢慢的,大家發(fā)現(xiàn),應(yīng)用服務(wù)器 CPU 都很正常了,但是還是有很多慢請(qǐng)求,究其原因,是因?yàn)閱吸c(diǎn)數(shù)據(jù)庫(kù)帶來(lái)了性能瓶頸。
于是程序員們決定使用主從結(jié)構(gòu)的數(shù)據(jù)庫(kù)集群,如下圖所示:
其中大部分讀操作可以直接訪問(wèn)從庫(kù),從而減輕主庫(kù)的壓力。然而這種方式還是無(wú)法解決寫(xiě)瓶頸,寫(xiě)依舊需要主庫(kù)來(lái)處理,當(dāng)業(yè)務(wù)量量級(jí)再次增高時(shí),寫(xiě)已經(jīng)變成刻不容緩的待處理瓶頸。
這時(shí)候,分庫(kù)分表方案出現(xiàn)了:
分庫(kù)分表不僅可以對(duì)相同的庫(kù)進(jìn)行拆分,還可以對(duì)相同的表進(jìn)行拆分,對(duì)表進(jìn)行拆分的方式叫做水平拆分。
不同功能的表放到不同的庫(kù)里,一般對(duì)應(yīng)的是垂直拆分(按照業(yè)務(wù)功能進(jìn)行拆分),此時(shí)一般還對(duì)應(yīng)了微服務(wù)化。
這種方法做到極致基本能支撐 TPS 在萬(wàn)級(jí)甚至更高的訪問(wèn)量了。然而隨著相同應(yīng)用擴(kuò)展的越多,每個(gè)數(shù)據(jù)庫(kù)的鏈接數(shù)也巨量增長(zhǎng),這讓數(shù)據(jù)庫(kù)本身的資源成為了瓶頸。
這個(gè)問(wèn)題產(chǎn)生的本質(zhì)是全量數(shù)據(jù)無(wú)差別的分享了所有的應(yīng)用資源,比如 A 用戶的請(qǐng)求在負(fù)載均衡的分配下可能分配到任意一個(gè)應(yīng)用服務(wù)器上,因而所有應(yīng)用全部都要鏈接 A 用戶所在的分庫(kù),數(shù)據(jù)庫(kù)連接數(shù)就變成笛卡爾乘積了。
在本質(zhì)點(diǎn)說(shuō),這種模式的資源隔離性還不夠徹底。要解決這個(gè)問(wèn)題,就需要把識(shí)別用戶分庫(kù)的邏輯往上層移動(dòng),從數(shù)據(jù)庫(kù)層移動(dòng)到路由網(wǎng)關(guān)層。
這樣一來(lái),從應(yīng)用服務(wù)器 a 進(jìn)來(lái)的來(lái)自 A 客戶的所有請(qǐng)求必然落庫(kù)到 DB-A,因此 a 也不用鏈接其他的數(shù)據(jù)庫(kù)實(shí)例了,這樣一個(gè)單元化的雛形就誕生了。
思考一下,應(yīng)用間其實(shí)也存在交互(比如 A 轉(zhuǎn)賬給 B),也就意味著,應(yīng)用不需要鏈接其他的數(shù)據(jù)庫(kù)了,但是還需要鏈接其他應(yīng)用。
如果是常見(jiàn)的 RPC 框架如 Dubbo 等,使用的是 TCP/IP 協(xié)議,那么等同于把之前與數(shù)據(jù)庫(kù)建立的鏈接,換成與其他應(yīng)用之間的鏈接了。
為啥這樣就消除瓶頸了呢?首先由于合理的設(shè)計(jì),應(yīng)用間的數(shù)據(jù)交互并不巨量,其次應(yīng)用間的交互可以共享 TCP 鏈接,比如 A->B 之間的 Socket 鏈接可以被 A 中的多個(gè)線程復(fù)用。
而一般的數(shù)據(jù)庫(kù)如 MySQL 則不行,所以 MySQL 才需要數(shù)據(jù)庫(kù)鏈接池。
如上圖所示,但我們把整套系統(tǒng)打包為單元化時(shí),每一類的數(shù)據(jù)從進(jìn)單元開(kāi)始就注定在這個(gè)單元被消化,由于這種徹底的隔離性,整個(gè)單元可以輕松的部署到任意機(jī)房而依然能保證邏輯上的統(tǒng)一。
下圖為一個(gè)三地五機(jī)房的部署方式:
螞蟻單元化架構(gòu)實(shí)踐
螞蟻支付寶應(yīng)該是國(guó)內(nèi)最大的支付工具,其在雙 11 等活動(dòng)日當(dāng)日的支付 TPS 可達(dá)幾十萬(wàn)級(jí),未來(lái)這個(gè)數(shù)字可能會(huì)更大,這決定了螞蟻單元化架構(gòu)從容量要求上看必然從單機(jī)房走向多機(jī)房。
另一方面,異地災(zāi)備也決定了這些 IDC 機(jī)房必須是異地部署的。整體上支付寶也采用了三地五中心(IDC 機(jī)房)來(lái)保障系統(tǒng)的可用性。
跟上文中描述的有所不同的是,支付寶將單元分成了三類(也稱 CRG 架構(gòu)):
RZone(Region Zone):直譯可能有點(diǎn)反而不好理解。實(shí)際上就是所有可以分庫(kù)分表的業(yè)務(wù)系統(tǒng)整體部署的最小單元。每個(gè) RZone 連上數(shù)據(jù)庫(kù)就可以撐起一片天空,把業(yè)務(wù)跑的溜溜的。
GZone(Global Zone):全局單元,意味著全局只有一份。部署了不可拆分的數(shù)據(jù)和服務(wù),比如系統(tǒng)配置等。
實(shí)際情況下,GZone 異地也會(huì)部署,不過(guò)僅是用于災(zāi)備,同一時(shí)刻,只有一地 GZone 進(jìn)行全局服務(wù)。GZone 一般被 RZone 依賴,提供的大部分是讀取服務(wù)。
CZone(City Zone):顧名思義,這是以城市為單位部署的單元。同樣部署了不可拆分的數(shù)據(jù)和服務(wù),比如用戶賬號(hào)服務(wù),客戶信息服務(wù)等。理論上 CZone 會(huì)被 RZone 以比訪問(wèn) GZone 高很多的頻率進(jìn)行訪問(wèn)。
CZone 是基于特定的 GZone 場(chǎng)景進(jìn)行優(yōu)化的一種單元,它把 GZone 中有些有著”寫(xiě)讀時(shí)間差現(xiàn)象”的數(shù)據(jù)和服務(wù)進(jìn)行了的單獨(dú)部署,這樣 RZone 只需要訪問(wèn)本地的 CZone 即可,而不是訪問(wèn)異地的 GZone。
“寫(xiě)讀時(shí)間差現(xiàn)象”是螞蟻架構(gòu)師們根據(jù)實(shí)踐統(tǒng)計(jì)總結(jié)的,他們發(fā)現(xiàn)大部分情況下,一個(gè)數(shù)據(jù)被寫(xiě)入后,都會(huì)過(guò)足夠長(zhǎng)的時(shí)間后才會(huì)被訪問(wèn)。
生活中這種例子很常見(jiàn),我們辦完銀行卡后可能很久才會(huì)存第一筆錢(qián);我們創(chuàng)建微博賬號(hào)后,可能想半天才會(huì)發(fā)微博;我們下載創(chuàng)建淘寶賬號(hào)后,可能得瀏覽好幾分鐘才會(huì)下單買(mǎi)東西。
當(dāng)然了這些例子中的時(shí)間差遠(yuǎn)遠(yuǎn)超過(guò)了系統(tǒng)同步時(shí)間。一般來(lái)說(shuō)異地的延時(shí)在 100ms 以內(nèi),所以只要滿足某地 CZone 寫(xiě)入數(shù)據(jù)后 100ms 以后才用這個(gè)數(shù)據(jù),這樣的數(shù)據(jù)和服務(wù)就適合放到 CZone 中。
相信大家看到這都會(huì)問(wèn):為啥分這三種單元?其實(shí)其背后對(duì)應(yīng)的是不同性質(zhì)的數(shù)據(jù),而服務(wù)不過(guò)是對(duì)數(shù)據(jù)的操作集。
下面我們來(lái)根據(jù)數(shù)據(jù)性質(zhì)的不同來(lái)解釋支付寶的 CRG 架構(gòu)。當(dāng)下幾乎所有互聯(lián)網(wǎng)公司的分庫(kù)分表規(guī)則都是根據(jù)用戶 ID 來(lái)制定的。
而圍繞用戶來(lái)看整個(gè)系統(tǒng)的數(shù)據(jù)可以分為以下兩類:
用戶流水型數(shù)據(jù):典型的有用戶的訂單、用戶發(fā)的評(píng)論、用戶的行為記錄等。
這些數(shù)據(jù)都是用戶行為產(chǎn)生的流水型數(shù)據(jù),具備天然的用戶隔離性,比如 A 用戶的 App 上絕對(duì)看不到 B 用戶的訂單列表。所以此類數(shù)據(jù)非常適合分庫(kù)分表后獨(dú)立部署服務(wù)。
用戶間共享型數(shù)據(jù):這種類型的數(shù)據(jù)又分兩類。一類共享型數(shù)據(jù)是像賬號(hào)、個(gè)人博客等可能會(huì)被所有用戶請(qǐng)求訪問(wèn)的用戶數(shù)據(jù)。
比如 A 向 B 轉(zhuǎn)賬,A 給 B 發(fā)消息,這時(shí)候需要確認(rèn) B 賬號(hào)是否存在;又比如 A 想看 B 的個(gè)人博客之類的。
另外一類是用戶無(wú)關(guān)型數(shù)據(jù),像商品、系統(tǒng)配置(匯率、優(yōu)惠政策)、財(cái)務(wù)統(tǒng)計(jì)等這些非用戶緯度的數(shù)據(jù),很難說(shuō)跟具體的某一類用戶掛鉤,可能涉及到所有用戶。
比如商品,假設(shè)按商品所在地來(lái)存放商品數(shù)據(jù)(這需要雙維度分庫(kù)分表),那么上海的用戶仍然需要訪問(wèn)杭州的商品。
這就又構(gòu)成跨地跨 Zone 訪問(wèn)了,還是達(dá)不到單元化的理想狀態(tài),而且雙維度分庫(kù)分表會(huì)給整個(gè) LDC 運(yùn)維帶來(lái)復(fù)雜度提升。
注:網(wǎng)上和支付寶內(nèi)部有另外一些分法,比如流水型和狀態(tài)性,有時(shí)候還會(huì)分為三類:流水型、狀態(tài)型和配置型。
個(gè)人覺(jué)得這些分法雖然嘗試去更高層次的抽象數(shù)據(jù)分類,但實(shí)際上邊界很模糊,適得其反。
直觀的類比,我們可以很輕易的將上述兩類數(shù)據(jù)對(duì)應(yīng)的服務(wù)劃分為 RZone 和 GZone,RZone 包含的就是分庫(kù)分表后負(fù)責(zé)固定客戶群體的服務(wù),GZone 則包含了用戶間共享的公共數(shù)據(jù)對(duì)應(yīng)的服務(wù)。
到這里為止,一切都很完美,這也是主流的單元化話題了。對(duì)比支付寶的 CRG 架構(gòu),我們一眼就發(fā)現(xiàn)少了 C(City Zone),CZone 確實(shí)是螞蟻在單元化實(shí)踐領(lǐng)域的一個(gè)創(chuàng)新點(diǎn)。
再來(lái)分析下 GZone,GZone 之所以只能單地部署,是因?yàn)槠鋽?shù)據(jù)要求被所有用戶共享,無(wú)法分庫(kù)分表,而多地部署會(huì)帶來(lái)由異地延時(shí)引起的不一致。
比如實(shí)時(shí)風(fēng)控系統(tǒng),如果多地部署,某個(gè) RZone 直接讀取本地的話,很容易讀取到舊的風(fēng)控狀態(tài),這是很危險(xiǎn)的。
這時(shí)螞蟻架構(gòu)師們問(wèn)了自己一個(gè)問(wèn)題——難道所有數(shù)據(jù)受不了延時(shí)么?這個(gè)問(wèn)題像是打開(kāi)了新世界的大門(mén),通過(guò)對(duì) RZone 已有業(yè)務(wù)的分析,架構(gòu)師們發(fā)現(xiàn) 80%?甚至更高的場(chǎng)景下,數(shù)據(jù)更新后都不要求立馬被讀取到。
也就是上文提到的”寫(xiě)讀時(shí)間差現(xiàn)象”,那么這就好辦了,對(duì)于這類數(shù)據(jù),我們?cè)试S每個(gè)地區(qū)的 RZone 服務(wù)直接訪問(wèn)本地,為了給這些 RZone 提供這些數(shù)據(jù)的本地訪問(wèn)能力,螞蟻架構(gòu)師設(shè)計(jì)出了 CZone。
在 CZone 的場(chǎng)景下,寫(xiě)請(qǐng)求一般從 GZone 寫(xiě)入公共數(shù)據(jù)所在庫(kù),然后同步到整個(gè) OB 集群,然后由 CZone 提供讀取服務(wù)。比如支付寶的會(huì)員服務(wù)就是如此。
即便架構(gòu)師們?cè)O(shè)計(jì)了完美的 CRG,但即便在螞蟻的實(shí)際應(yīng)用中,各個(gè)系統(tǒng)仍然存在不合理的 CRG 分類,尤其是 CG 不分的現(xiàn)象很常見(jiàn)。
支付寶單元化的異地多活和災(zāi)備
流量挑撥技術(shù)探秘簡(jiǎn)介
單元化后,異地多活只是多地部署而已。比如上海的兩個(gè)單元為 ID 范圍為 [00~19],[40~59]?的用戶服務(wù)。
而杭州的兩個(gè)單元為 ID 為 [20~39]和[60,79]的用戶服務(wù),這樣上海和杭州就是異地雙活的。
支付寶對(duì)單元化的基本要求是每個(gè)單元都具備服務(wù)所有用戶的能力,即——具體的那個(gè)單元服務(wù)哪些用戶是可以動(dòng)態(tài)配置的。所以異地雙活的這些單元還充當(dāng)了彼此的備份。
發(fā)現(xiàn)工作中冷備熱備已經(jīng)被用的很亂了。最早冷備是指數(shù)據(jù)庫(kù)在備份數(shù)據(jù)時(shí)需要關(guān)閉后進(jìn)行備份(也叫離線備份),防止數(shù)據(jù)備份過(guò)程中又修改了,不需要關(guān)閉即在運(yùn)行過(guò)程中進(jìn)行數(shù)據(jù)備份的方式叫做熱備(也叫在線備份)。
也不知道從哪一天開(kāi)始,冷備在主備系統(tǒng)里代表了這臺(tái)備用機(jī)器是關(guān)閉狀態(tài)的,只有主服務(wù)器掛了之后,備服務(wù)器才會(huì)被啟動(dòng)。
而相同的熱備變成了備服務(wù)器也是啟動(dòng)的,只是沒(méi)有流量而已,一旦主服務(wù)器掛了之后,流量自動(dòng)打到備服務(wù)器上。本文不打算用第二種理解,因?yàn)楦杏X(jué)有點(diǎn)野。
為了做到每個(gè)單元訪問(wèn)哪些用戶變成可配置,支付寶要求單元化管理系統(tǒng)具備流量到單元的可配置以及單元到 DB 的可配置能力。
如下圖所示:
其中 Spanner 是螞蟻基于 Nginx 自研的反向代理網(wǎng)關(guān),也很好理解,有些請(qǐng)求我們希望在反向代理層就被轉(zhuǎn)發(fā)至其他 IDC 的 Spanner 而無(wú)需進(jìn)入后端服務(wù),如圖箭頭 2 所示。
那么對(duì)于應(yīng)該在本 IDC 處理的請(qǐng)求,就直接映射到對(duì)應(yīng)的 RZ 即可,如圖箭頭 1。
進(jìn)入后端服務(wù)后,理論上如果請(qǐng)求只是讀取用戶流水型數(shù)據(jù),那么一般不會(huì)再進(jìn)行路由了。
然而,對(duì)于有些場(chǎng)景來(lái)說(shuō),A 用戶的一個(gè)請(qǐng)求可能關(guān)聯(lián)了對(duì) B 用戶數(shù)據(jù)的訪問(wèn),比如 A 轉(zhuǎn)賬給 B,A 扣完錢(qián)后要調(diào)用賬務(wù)系統(tǒng)去增加 B 的余額。
這時(shí)候就涉及到再次的路由,同樣有兩個(gè)結(jié)果:跳轉(zhuǎn)到其他 IDC(如圖箭頭 3)或是跳轉(zhuǎn)到本 IDC 的其他 RZone(如圖箭頭 4)。
RZone 到 DB 數(shù)據(jù)分區(qū)的訪問(wèn)這是事先配置好的,上圖中 RZ 和 DB 數(shù)據(jù)分區(qū)的關(guān)系為:
RZ0*?-->?a RZ1*?-->?b RZ2*?-->?c RZ3*?-->?d下面我們舉個(gè)例子來(lái)說(shuō)明整個(gè)流量挑撥的過(guò)程,假設(shè) C 用戶所屬的數(shù)據(jù)分區(qū)是 c,而 C 用戶在杭州訪問(wèn)了 cashier.alipay.com(隨便編的)。
①目前支付寶默認(rèn)會(huì)按照地域來(lái)路由流量,具體的實(shí)現(xiàn)承載者是自研的 GLSB(Global Server Load Balancing):
https:它會(huì)根據(jù)請(qǐng)求者的 IP,自動(dòng)將 cashier.alipay.com 解析為杭州 IDC 的 IP 地址(或者跳轉(zhuǎn)到 IDC 所在的域名)。
大家自己搞過(guò)網(wǎng)站的化應(yīng)該知道大部分 DNS 服務(wù)商的地址都是靠人去配置的,GLSB 屬于動(dòng)態(tài)配置域名的系統(tǒng),網(wǎng)上也有比較火的類似產(chǎn)品,比如花生殼之類(建過(guò)私站的同學(xué)應(yīng)該很熟悉)的。
②好了,到此為止,用戶的請(qǐng)求來(lái)到了 IDC-1 的 Spanner 集群服務(wù)器上,Spanner 從內(nèi)存中讀取到了路由配置,知道了這個(gè)請(qǐng)求的主體用戶 C 所屬的 RZ3* 不再本 IDC,于是直接轉(zhuǎn)到了 IDC-2 進(jìn)行處理。
③進(jìn)入 IDC-2 之后,根據(jù)流量配比規(guī)則,該請(qǐng)求被分配到了 RZ3B 進(jìn)行處理。
④RZ3B 得到請(qǐng)求后對(duì)數(shù)據(jù)分區(qū) c 進(jìn)行訪問(wèn)。
⑤處理完畢后原路返回。
大家應(yīng)該發(fā)現(xiàn)問(wèn)題所在了,如果再來(lái)一個(gè)這樣的請(qǐng)求,豈不是每次都要跨地域進(jìn)行調(diào)用和返回體傳遞?
確實(shí)是存在這樣的問(wèn)題的,對(duì)于這種問(wèn)題,支付寶架構(gòu)師們決定繼續(xù)把決策邏輯往用戶終端推移。
比如,每個(gè) IDC 機(jī)房都會(huì)有自己的域名(真實(shí)情況可能不是這樣命名的):?
IDC-1?對(duì)應(yīng)?cashieridc-1.alipay.com
IDC-2 對(duì)應(yīng)?cashieridc-2.alipay.com
那么請(qǐng)求從 IDC-1 涮過(guò)一遍返回時(shí)會(huì)將前端請(qǐng)求跳轉(zhuǎn)到 cashieridc-2.alipay.com 去(如果是 App,只需要替換 rest 調(diào)用的接口域名),后面所有用戶的行為都會(huì)在這個(gè)域名上發(fā)生,就避免了走一遍 IDC-1 帶來(lái)的延時(shí)。
支付寶災(zāi)備機(jī)制
流量挑撥是災(zāi)備切換的基礎(chǔ)和前提條件,發(fā)生災(zāi)難后的通用方法就是把陷入災(zāi)難的單元的流量重新打到正常的單元上去,這個(gè)流量切換的過(guò)程俗稱切流。
支付寶 LDC 架構(gòu)下的災(zāi)備有三個(gè)層次:
同機(jī)房單元間災(zāi)備
同城機(jī)房間災(zāi)備
異地機(jī)房間災(zāi)備
同機(jī)房單元間災(zāi)備:災(zāi)難發(fā)生可能性相對(duì)最高(但其實(shí)也很小)。對(duì) LDC 來(lái)說(shuō),最小的災(zāi)難就是某個(gè)單元由于一些原因(局部插座斷開(kāi)、線路老化、人為操作失誤)宕機(jī)了。
從上節(jié)里的圖中可以看到每組 RZ 都有 A,B 兩個(gè)單元,這就是用來(lái)做同機(jī)房災(zāi)備的,并且 AB 之間也是雙活雙備的。
正常情況下 AB 兩個(gè)單元共同分擔(dān)所有的請(qǐng)求,一旦 A 單元掛了,B 單元將自動(dòng)承擔(dān) A 單元的流量份額。這個(gè)災(zāi)備方案是默認(rèn)的。
同城機(jī)房間災(zāi)備:災(zāi)難發(fā)生可能性相對(duì)更小。這種災(zāi)難發(fā)生的原因一般是機(jī)房電線網(wǎng)線被挖斷,或者機(jī)房維護(hù)人員操作失誤導(dǎo)致的。
在這種情況下,就需要人工的制定流量挑撥(切流)方案了。下面我們舉例說(shuō)明這個(gè)過(guò)程,如下圖所示為上海的兩個(gè) IDC 機(jī)房。
整個(gè)切流配置過(guò)程分兩步,首先需要將陷入災(zāi)難的機(jī)房中 RZone 對(duì)應(yīng)的數(shù)據(jù)分區(qū)的訪問(wèn)權(quán)配置進(jìn)行修改。
假設(shè)我們的方案是由 IDC-2 機(jī)房的 RZ2 和 RZ3 分別接管 IDC-1 中的 RZ0 和 RZ1。
那么首先要做的是把數(shù)據(jù)分區(qū) a,b 對(duì)應(yīng)的訪問(wèn)權(quán)從 RZ0 和 RZ1 收回,分配給 RZ2 和 RZ3。
即將(如上圖所示為初始映射):
RZ0*?-->?a RZ1*?-->?b RZ2*?-->?c RZ3*?-->?d變?yōu)?#xff1a;
RZ0*?-->?/ RZ1*?-->?/ RZ2*?-->?a RZ2*?-->?c RZ3*?-->?b RZ3*?-->?d然后再修改用戶 ID 和 RZ 之間的映射配置。假設(shè)之前為:
[00-24]?-->?RZ0A(50%),RZOB(50%) [25-49]?-->?RZ1A(50%),RZ1B(50%) [50-74]?-->?RZ2A(50%),RZ2B(50%) [75-99]?-->?RZ3A(50%),RZ3B(50%)那么按照災(zāi)備方案的要求,這個(gè)映射配置將變?yōu)?#xff1a;
[00-24]?-->?RZ2A(50%),RZ2B(50%) [25-49]?-->?RZ3A(50%),RZ3B(50%) [50-74]?-->?RZ2A(50%),RZ2B(50%) [75-99]?-->?RZ3A(50%),RZ3B(50%)這樣之后,所有流量將會(huì)被打到 IDC-2 中,期間部分已經(jīng)向 IDC-1 發(fā)起請(qǐng)求的用戶會(huì)收到失敗并重試的提示。
實(shí)際情況中,整個(gè)過(guò)程并不是災(zāi)難發(fā)生后再去做的,整個(gè)切換的流程會(huì)以預(yù)案配置的形式事先準(zhǔn)備好,推送給每個(gè)流量挑撥客戶端(集成到了所有的服務(wù)和 Spanner 中)。
這里可以思考下,為何先切數(shù)據(jù)庫(kù)映射,再切流量呢?這是因?yàn)槿绻惹辛髁?#xff0c;意味著大量注定失敗的請(qǐng)求會(huì)被打到新的正常單元上去,從而影響系統(tǒng)的穩(wěn)定性(數(shù)據(jù)庫(kù)還沒(méi)準(zhǔn)備好)。
異地機(jī)房間災(zāi)備:這個(gè)基本上跟同城機(jī)房間災(zāi)備一致(這也是單元化的優(yōu)點(diǎn)),不再贅述。
螞蟻單元化架構(gòu)的?CAP?分析
回顧 CAP
①CAP 的定義
CAP 原則是指任意一個(gè)分布式系統(tǒng),同時(shí)最多只能滿足其中的兩項(xiàng),而無(wú)法同時(shí)滿足三項(xiàng)。
所謂的分布式系統(tǒng),說(shuō)白了就是一件事一個(gè)人做的,現(xiàn)在分給好幾個(gè)人一起干。
我們先簡(jiǎn)單回顧下 CAP 各個(gè)維度的含義:
Consistency(一致性),這個(gè)理解起來(lái)很簡(jiǎn)單,就是每時(shí)每刻每個(gè)節(jié)點(diǎn)上的同一份數(shù)據(jù)都是一致的。
這就要求任何更新都是原子的,即要么全部成功,要么全部失敗。想象一下使用分布式事務(wù)來(lái)保證所有系統(tǒng)的原子性是多么低效的一個(gè)操作。
Availability(可用性),這個(gè)可用性看起來(lái)很容易理解,但真正說(shuō)清楚的不多。我更愿意把可用性解釋為:任意時(shí)刻系統(tǒng)都可以提供讀寫(xiě)服務(wù)。
舉個(gè)例子,當(dāng)我們用事務(wù)將所有節(jié)點(diǎn)鎖住來(lái)進(jìn)行某種寫(xiě)操作時(shí),如果某個(gè)節(jié)點(diǎn)發(fā)生不可用的情況,會(huì)讓整個(gè)系統(tǒng)不可用。
對(duì)于分片式的 NoSQL 中間件集群(Redis,Memcached)來(lái)說(shuō),一旦一個(gè)分片歇菜了,整個(gè)系統(tǒng)的數(shù)據(jù)也就不完整了,讀取宕機(jī)分片的數(shù)據(jù)就會(huì)沒(méi)響應(yīng),也就是不可用了。
需要說(shuō)明一點(diǎn),哪些選擇 CP 的分布式系統(tǒng),并不是代表可用性就完全沒(méi)有了,只是可用性沒(méi)有保障了。
為了增加可用性保障,這類中間件往往都提供了”分片集群+復(fù)制集”的方案。
Partition tolerance(分區(qū)容忍性),這個(gè)可能也是很多文章都沒(méi)說(shuō)清楚的。P 并不是像 CA 一樣是一個(gè)獨(dú)立的性質(zhì),它依托于 CA 來(lái)進(jìn)行討論。
參考文獻(xiàn)中的解釋:”除非整個(gè)網(wǎng)絡(luò)癱瘓,否則任何時(shí)刻系統(tǒng)都能正常工作”,言下之意是小范圍的網(wǎng)絡(luò)癱瘓,節(jié)點(diǎn)宕機(jī),都不會(huì)影響整個(gè)系統(tǒng)的 CA。
我感覺(jué)這個(gè)解釋聽(tīng)著還是有點(diǎn)懵逼,所以個(gè)人更愿意解釋為當(dāng)節(jié)點(diǎn)之間網(wǎng)絡(luò)不通時(shí)(出現(xiàn)網(wǎng)絡(luò)分區(qū)),可用性和一致性仍然能得到保障。
從個(gè)人角度理解,分區(qū)容忍性又分為“可用性分區(qū)容忍性”和“一致性分區(qū)容忍性”。
出現(xiàn)分區(qū)時(shí)會(huì)不會(huì)影響可用性的關(guān)鍵在于需不需要所有節(jié)點(diǎn)互相溝通協(xié)作來(lái)完成一次事務(wù),不需要的話是鐵定不影響可用性的。
慶幸的是應(yīng)該不太會(huì)有分布式系統(tǒng)會(huì)被設(shè)計(jì)成完成一次事務(wù)需要所有節(jié)點(diǎn)聯(lián)動(dòng),一定要舉個(gè)例子的話,全同步復(fù)制技術(shù)下的 MySQL 是一個(gè)典型案例。
出現(xiàn)分區(qū)時(shí)會(huì)不會(huì)影響一致性的關(guān)鍵則在于出現(xiàn)腦裂時(shí)有沒(méi)有保證一致性的方案,這對(duì)主從同步型數(shù)據(jù)庫(kù)(MySQL、SQL Server)是致命的。
一旦網(wǎng)絡(luò)出現(xiàn)分區(qū),產(chǎn)生腦裂,系統(tǒng)會(huì)出現(xiàn)一份數(shù)據(jù)兩個(gè)值的狀態(tài),誰(shuí)都不覺(jué)得自己是錯(cuò)的。
需要說(shuō)明的是,正常來(lái)說(shuō)同一局域網(wǎng)內(nèi),網(wǎng)絡(luò)分區(qū)的概率非常低,這也是為啥我們最熟悉的數(shù)據(jù)庫(kù)(MySQL、SQL Server 等)也是不考慮 P 的原因。
下圖為 CAP 之間的經(jīng)典關(guān)系圖:
還有個(gè)需要說(shuō)明的地方,其實(shí)分布式系統(tǒng)很難滿足 CAP 的前提條件是這個(gè)系統(tǒng)一定是有讀有寫(xiě)的,如果只考慮讀,那么 CAP 很容易都滿足。
比如一個(gè)計(jì)算器服務(wù),接受表達(dá)式請(qǐng)求,返回計(jì)算結(jié)果,搞成水平擴(kuò)展的分布式,顯然這樣的系統(tǒng)沒(méi)有一致性問(wèn)題,網(wǎng)絡(luò)分區(qū)也不怕,可用性也是很穩(wěn)的,所以可以滿足 CAP。
②CAP 分析方法
先說(shuō)下 CA 和 P 的關(guān)系,如果不考慮 P 的話,系統(tǒng)是可以輕松實(shí)現(xiàn) CA 的。
而 P 并不是一個(gè)單獨(dú)的性質(zhì),它代表的是目標(biāo)分布式系統(tǒng)有沒(méi)有對(duì)網(wǎng)絡(luò)分區(qū)的情況做容錯(cuò)處理。
如果做了處理,就一定是帶有 P 的,接下來(lái)再考慮分區(qū)情況下到底選擇了 A 還是 C。所以分析 CAP,建議先確定有沒(méi)有對(duì)分區(qū)情況做容錯(cuò)處理。
以下是個(gè)人總結(jié)的分析一個(gè)分布式系統(tǒng) CAP 滿足情況的一般方法:
if( 不存在分區(qū)的可能性 ||?分區(qū)后不影響可用性或一致性 ||?有影響但考慮了分區(qū)情況-P){if(可用性分區(qū)容忍性-A under P))return?"AP";else?if(一致性分區(qū)容忍性-C under P)return?"CP"; } else{ //分區(qū)有影響但沒(méi)考慮分區(qū)情況下的容錯(cuò)if(具備可用性-A && 具備一致性-C){return?AC;} }這里說(shuō)明下,如果考慮了分區(qū)容忍性,就不需要考慮不分區(qū)情況下的可用性和一致性了(大多是滿足的)。
水平擴(kuò)展應(yīng)用+單數(shù)據(jù)庫(kù)實(shí)例的 CAP?分析
讓我們?cè)賮?lái)回顧下分布式應(yīng)用系統(tǒng)的來(lái)由,早年每個(gè)應(yīng)用都是單體的,跑在一個(gè)服務(wù)器上,服務(wù)器一掛,服務(wù)就不可用了。
另外一方面,單體應(yīng)用由于業(yè)務(wù)功能復(fù)雜,對(duì)機(jī)器的要求也逐漸變高,普通的微機(jī)無(wú)法滿足這種性能和容量的要求。
所以要拆!還在 IBM 大賣小型商用機(jī)的年代,阿里巴巴就提出要以分布式微機(jī)替代小型機(jī)。
所以我們發(fā)現(xiàn),分布式系統(tǒng)解決的最大的痛點(diǎn),就是單體單機(jī)系統(tǒng)的可用性問(wèn)題。
要想高可用,必須分布式。一家互聯(lián)網(wǎng)公司的發(fā)展之路上,第一次與分布式相遇應(yīng)該都是在單體應(yīng)用的水平擴(kuò)展上。
也就是同一個(gè)應(yīng)用啟動(dòng)了多個(gè)實(shí)例,連接著相同的數(shù)據(jù)庫(kù)(為了簡(jiǎn)化問(wèn)題,先不考慮數(shù)據(jù)庫(kù)是否單點(diǎn)),如下圖所示:
這樣的系統(tǒng)天然具有的就是 AP(可用性和分區(qū)容忍性):
一方面解決了單點(diǎn)導(dǎo)致的低可用性問(wèn)題。
另一方面無(wú)論這些水平擴(kuò)展的機(jī)器間網(wǎng)絡(luò)是否出現(xiàn)分區(qū),這些服務(wù)器都可以各自提供服務(wù),因?yàn)樗麄冎g不需要進(jìn)行溝通。
然而,這樣的系統(tǒng)是沒(méi)有一致性可言的,想象一下每個(gè)實(shí)例都可以往數(shù)據(jù)庫(kù) insert 和 update(注意這里還沒(méi)討論到事務(wù)),那還不亂了套。
于是我們轉(zhuǎn)向了讓 DB 去做這個(gè)事,這時(shí)候”數(shù)據(jù)庫(kù)事務(wù)”就被用上了。用大部分公司會(huì)選擇的 MySQL 來(lái)舉例,用了事務(wù)之后會(huì)發(fā)現(xiàn)數(shù)據(jù)庫(kù)又變成了單點(diǎn)和瓶頸。
單點(diǎn)就像單機(jī)一樣(本例子中不考慮從庫(kù)模式),理論上就不叫分布式了,如果一定要分析其 CAP 的話,根據(jù)上面的步驟分析過(guò)程應(yīng)該是這樣的:
分區(qū)容忍性:先看有沒(méi)有考慮分區(qū)容忍性,或者分區(qū)后是否會(huì)有影響。單臺(tái) MySQL 無(wú)法構(gòu)成分區(qū),要么整個(gè)系統(tǒng)掛了,要么就活著。
可用性分區(qū)容忍性:分區(qū)情況下,假設(shè)恰好是該節(jié)點(diǎn)掛了,系統(tǒng)也就不可用了,所以可用性分區(qū)容忍性不滿足。
一致性分區(qū)容忍性:分區(qū)情況下,只要可用,單點(diǎn)單機(jī)的最大好處就是一致性可以得到保障。
因此這樣的一個(gè)系統(tǒng),個(gè)人認(rèn)為只是滿足了 CP。A 有但不出色,從這點(diǎn)可以看出,CAP 并不是非黑即白的。
包括常說(shuō)的 BASE (最終一致性)方案,其實(shí)只是 C 不出色,但最終也是達(dá)到一致性的,BASE 在一致性上選擇了退讓。
關(guān)于分布式應(yīng)用+單點(diǎn)數(shù)據(jù)庫(kù)的模式算不算純正的分布式系統(tǒng),這個(gè)可能每個(gè)人看法有點(diǎn)差異,上述只是我個(gè)人的一種理解,是不是分布式系統(tǒng)不重要,重要的是分析過(guò)程。
其實(shí)我們討論分布式,就是希望系統(tǒng)的可用性是多個(gè)系統(tǒng)多活的,一個(gè)掛了另外的也能頂上,顯然單機(jī)單點(diǎn)的系統(tǒng)不具備這樣的高可用特性。
所以在我看來(lái),廣義的說(shuō) CAP 也適用于單點(diǎn)單機(jī)系統(tǒng),單機(jī)系統(tǒng)是 CP 的。
說(shuō)到這里,大家似乎也發(fā)現(xiàn)了,水平擴(kuò)展的服務(wù)應(yīng)用+數(shù)據(jù)庫(kù)這樣的系統(tǒng)的 CAP 魔咒主要發(fā)生在數(shù)據(jù)庫(kù)層。
因?yàn)榇蟛糠诌@樣的服務(wù)應(yīng)用都只是承擔(dān)了計(jì)算的任務(wù)(像計(jì)算器那樣),本身不需要互相協(xié)作,所有寫(xiě)請(qǐng)求帶來(lái)的數(shù)據(jù)的一致性問(wèn)題下沉到了數(shù)據(jù)庫(kù)層去解決。
想象一下,如果沒(méi)有數(shù)據(jù)庫(kù)層,而是應(yīng)用自己來(lái)保障數(shù)據(jù)一致性,那么這樣的應(yīng)用之間就涉及到狀態(tài)的同步和交互了,ZooKeeper 就是這么一個(gè)典型的例子。
水平擴(kuò)展應(yīng)用+主從數(shù)據(jù)庫(kù)集群的CAP分析
上一節(jié)我們討論了多應(yīng)用實(shí)例+單數(shù)據(jù)庫(kù)實(shí)例的模式,這種模式是分布式系統(tǒng)也好,不是分布式系統(tǒng)也罷,整體是偏 CP 的。
現(xiàn)實(shí)中,技術(shù)人員們也會(huì)很快發(fā)現(xiàn)這種架構(gòu)的不合理性——可用性太低了。于是如下圖所示的模式成為了當(dāng)下大部分中小公司所使用的架構(gòu):
從上圖我可以看到三個(gè)數(shù)據(jù)庫(kù)實(shí)例中只有一個(gè)是主庫(kù),其他是從庫(kù)。
一定程度上,這種架構(gòu)極大的緩解了”讀可用性”問(wèn)題,而這樣的架構(gòu)一般會(huì)做讀寫(xiě)分離來(lái)達(dá)到更高的”讀可用性”,幸運(yùn)的是大部分互聯(lián)網(wǎng)場(chǎng)景中讀都占了 80% 以上,所以這樣的架構(gòu)能得到較長(zhǎng)時(shí)間的廣泛應(yīng)用。
寫(xiě)可用性可以通過(guò) Keepalived 這種 HA(高可用)框架來(lái)保證主庫(kù)是活著的,但仔細(xì)一想就可以明白,這種方式并沒(méi)有帶來(lái)性能上的可用性提升。還好,至少系統(tǒng)不會(huì)因?yàn)槟硞€(gè)實(shí)例掛了就都不可用了。
可用性勉強(qiáng)達(dá)標(biāo)了,這時(shí)候的 CAP 分析如下:
分區(qū)容忍性:依舊先看分區(qū)容忍性,主從結(jié)構(gòu)的數(shù)據(jù)庫(kù)存在節(jié)點(diǎn)之間的通信,他們之間需要通過(guò)心跳來(lái)保證只有一個(gè) Master。
然而一旦發(fā)生分區(qū),每個(gè)分區(qū)會(huì)自己選取一個(gè)新的 Master,這樣就出現(xiàn)了腦裂,常見(jiàn)的主從數(shù)據(jù)庫(kù)(MySQL,Oracle 等)并沒(méi)有自帶解決腦裂的方案。所以分區(qū)容忍性是沒(méi)考慮的。
一致性:不考慮分區(qū),由于任意時(shí)刻只有一個(gè)主庫(kù),所以一致性是滿足的。
可用性:不考慮分區(qū),HA 機(jī)制的存在可以保證可用性,所以可用性顯然也是滿足的。
所以這樣的一個(gè)系統(tǒng),我們認(rèn)為它是 AC 的。我們?cè)偕钊胙芯肯?#xff0c;如果發(fā)生腦裂產(chǎn)生數(shù)據(jù)不一致后有一種方式可以仲裁一致性問(wèn)題,是不是就可以滿足 P 了呢。
還真有嘗試通過(guò)預(yù)先設(shè)置規(guī)則來(lái)解決這種多主庫(kù)帶來(lái)的一致性問(wèn)題的系統(tǒng),比如 CouchDB,它通過(guò)版本管理來(lái)支持多庫(kù)寫(xiě)入,在其仲裁階段會(huì)通過(guò) DBA 配置的仲裁規(guī)則(也就是合并規(guī)則,比如誰(shuí)的時(shí)間戳最晚誰(shuí)的生效)進(jìn)行自動(dòng)仲裁(自動(dòng)合并),從而保障最終一致性(BASE),自動(dòng)規(guī)則無(wú)法合并的情況則只能依賴人工決策了。
螞蟻單元化 LDC 架構(gòu) CAP 分析
①戰(zhàn)勝分區(qū)容忍性
在討論螞蟻 LDC 架構(gòu)的 CAP 之前,我們?cè)賮?lái)想想分區(qū)容忍性有啥值得一提的,為啥很多大名鼎鼎的 BASE(最終一致性)體系系統(tǒng)都選擇損失實(shí)時(shí)一致性,而不是丟棄分區(qū)容忍性呢?
分區(qū)的產(chǎn)生一般有兩種情況:
某臺(tái)機(jī)器宕機(jī)了,過(guò)一會(huì)兒又重啟了,看起來(lái)就像失聯(lián)了一段時(shí)間,像是網(wǎng)絡(luò)不可達(dá)一樣。
異地部署情況下,異地多活意味著每一地都可能會(huì)產(chǎn)生數(shù)據(jù)寫(xiě)入,而異地之間偶爾的網(wǎng)絡(luò)延時(shí)尖刺(網(wǎng)絡(luò)延時(shí)曲線圖陡增)、網(wǎng)絡(luò)故障都會(huì)導(dǎo)致小范圍的網(wǎng)絡(luò)分區(qū)產(chǎn)生。
前文也提到過(guò),如果一個(gè)分布式系統(tǒng)是部署在一個(gè)局域網(wǎng)內(nèi)的(一個(gè)物理機(jī)房?jī)?nèi)),那么個(gè)人認(rèn)為分區(qū)的概率極低,即便有復(fù)雜的拓?fù)?#xff0c;也很少會(huì)有在同一個(gè)機(jī)房里出現(xiàn)網(wǎng)絡(luò)分區(qū)的情況。
而異地這個(gè)概率會(huì)大大增高,所以螞蟻的三地五中心必須需要思考這樣的問(wèn)題,分區(qū)容忍不能丟!
同樣的情況還會(huì)發(fā)生在不同 ISP 的機(jī)房之間(想象一下你和朋友組隊(duì)玩 DOTA,他在電信,你在聯(lián)通)。
為了應(yīng)對(duì)某一時(shí)刻某個(gè)機(jī)房突發(fā)的網(wǎng)絡(luò)延時(shí)尖刺活著間歇性失聯(lián),一個(gè)好的分布式系統(tǒng)一定能處理好這種情況下的一致性問(wèn)題。
那么螞蟻是怎么解決這個(gè)問(wèn)題的呢?我們?cè)谏衔挠懻撨^(guò),其實(shí) LDC 機(jī)房的各個(gè)單元都由兩部分組成:負(fù)責(zé)業(yè)務(wù)邏輯計(jì)算的應(yīng)用服務(wù)器和負(fù)責(zé)數(shù)據(jù)持久化的數(shù)據(jù)庫(kù)。
大部分應(yīng)用服務(wù)器就像一個(gè)個(gè)計(jì)算器,自身是不對(duì)寫(xiě)一致性負(fù)責(zé)的,這個(gè)任務(wù)被下沉到了數(shù)據(jù)庫(kù)。所以螞蟻解決分布式一致性問(wèn)題的關(guān)鍵就在于數(shù)據(jù)庫(kù)!
想必螞蟻的讀者大概猜到下面的討論重點(diǎn)了——OceanBase(下文簡(jiǎn)稱OB),中國(guó)第一款自主研發(fā)的分布式數(shù)據(jù)庫(kù),一時(shí)間也確實(shí)獲得了很多光環(huán)。
在討論 OB 前,我們先來(lái)想想 Why not MySQL?
首先,就像 CAP 三角圖中指出的,MySQL 是一款滿足 AC 但不滿足 P 的分布式系統(tǒng)。
試想一下,一個(gè) MySQL 主從結(jié)構(gòu)的數(shù)據(jù)庫(kù)集群,當(dāng)出現(xiàn)分區(qū)時(shí),問(wèn)題分區(qū)內(nèi)的 Slave 會(huì)認(rèn)為主已經(jīng)掛了,所以自己成為本分區(qū)的 Master(腦裂)。
等分區(qū)問(wèn)題恢復(fù)后,會(huì)產(chǎn)生 2 個(gè)主庫(kù)的數(shù)據(jù),而無(wú)法確定誰(shuí)是正確的,也就是分區(qū)導(dǎo)致了一致性被破壞。這樣的結(jié)果是嚴(yán)重的,這也是螞蟻寧愿自研 OceanBase 的原動(dòng)力之一。
那么如何才能讓分布式系統(tǒng)具備分區(qū)容忍性呢?按照老慣例,我們從”可用性分區(qū)容忍”和”一致性分區(qū)容忍”兩個(gè)方面來(lái)討論:
可用性分區(qū)容忍性保障機(jī)制:可用性分區(qū)容忍的關(guān)鍵在于別讓一個(gè)事務(wù)一來(lái)所有節(jié)點(diǎn)來(lái)完成,這個(gè)很簡(jiǎn)單,別要求所有節(jié)點(diǎn)共同同時(shí)參與某個(gè)事務(wù)即可。
一致性分區(qū)容忍性保障機(jī)制:老實(shí)說(shuō),都產(chǎn)生分區(qū)了,哪還可能獲得實(shí)時(shí)一致性。
但要保證最終一致性也不簡(jiǎn)單,一旦產(chǎn)生分區(qū),如何保證同一時(shí)刻只會(huì)產(chǎn)生一份提議呢?
換句話說(shuō),如何保障仍然只有一個(gè)腦呢?下面我們來(lái)看下 PAXOS 算法是如何解決腦裂問(wèn)題的。
這里可以發(fā)散下,所謂的“腦”其實(shí)就是具備寫(xiě)能力的系統(tǒng),“非腦”就是只具備讀能力的系統(tǒng),對(duì)應(yīng)了 MySQL 集群中的從庫(kù)。
下面是一段摘自維基百科的 PAXOS 定義:
Paxos is a family of protocols for solving consensus in a network of unreliable processors (that is, processors that may fail).
大致意思就是說(shuō),PAXOS 是在一群不是特別可靠的節(jié)點(diǎn)組成的集群中的一種共識(shí)機(jī)制。
Paxos 要求任何一個(gè)提議,至少有 (N/2)+1 的系統(tǒng)節(jié)點(diǎn)認(rèn)可,才被認(rèn)為是可信的,這背后的一個(gè)基礎(chǔ)理論是少數(shù)服從多數(shù)。
想象一下,如果多數(shù)節(jié)點(diǎn)認(rèn)可后,整個(gè)系統(tǒng)宕機(jī)了,重啟后,仍然可以通過(guò)一次投票知道哪個(gè)值是合法的(多數(shù)節(jié)點(diǎn)保留的那個(gè)值)。
這樣的設(shè)定也巧妙的解決了分區(qū)情況下的共識(shí)問(wèn)題,因?yàn)橐坏┊a(chǎn)生分區(qū),勢(shì)必最多只有一個(gè)分區(qū)內(nèi)的節(jié)點(diǎn)數(shù)量會(huì)大于等于 (N/2)+1。
通過(guò)這樣的設(shè)計(jì)就可以巧妙的避開(kāi)腦裂,當(dāng)然 MySQL 集群的腦裂問(wèn)題也是可以通過(guò)其他方法來(lái)解決的,比如同時(shí) Ping 一個(gè)公共的 IP,成功者繼續(xù)為腦,顯然這就又制造了另外一個(gè)單點(diǎn)。
如果你了解過(guò)比特幣或者區(qū)塊鏈,你就知道區(qū)塊鏈的基礎(chǔ)理論也是 PAXOS。區(qū)塊鏈借助 PAXOS 對(duì)最終一致性的貢獻(xiàn)來(lái)抵御惡意篡改。
而本文涉及的分布式應(yīng)用系統(tǒng)則是通過(guò) PAXOS 來(lái)解決分區(qū)容忍性。再說(shuō)本質(zhì)一點(diǎn),一個(gè)是抵御部分節(jié)點(diǎn)變壞,一個(gè)是防范部分節(jié)點(diǎn)失聯(lián)。
大家一定聽(tīng)說(shuō)過(guò)這樣的描述:PAXOS 是唯一能解決分布式一致性問(wèn)題的解法。
這句話越是理解越發(fā)覺(jué)得詭異,這會(huì)讓人以為 PAXOS 逃離于 CAP 約束了,所以個(gè)人更愿意理解為:PAXOS 是唯一一種保障分布式系統(tǒng)最終一致性的共識(shí)算法(所謂共識(shí)算法,就是大家都按照這個(gè)算法來(lái)操作,大家最后的結(jié)果一定相同)。
PAXOS 并沒(méi)有逃離 CAP 魔咒,畢竟達(dá)成共識(shí)是 (N/2)+1 的節(jié)點(diǎn)之間的事,剩下的 (N/2)-1 的節(jié)點(diǎn)上的數(shù)據(jù)還是舊的,這時(shí)候仍然是不一致的。
所以 PAXOS 對(duì)一致性的貢獻(xiàn)在于經(jīng)過(guò)一次事務(wù)后,這個(gè)集群里已經(jīng)有部分節(jié)點(diǎn)保有了本次事務(wù)正確的結(jié)果(共識(shí)的結(jié)果),這個(gè)結(jié)果隨后會(huì)被異步的同步到其他節(jié)點(diǎn)上,從而保證最終一致性。
以下摘自維基百科:
Paxos is a family of protocols for solving consensus in a network of unreliable processors (that is, processors that may fail).Quorums express the safety (or consistency) properties of Paxos by ensuring at least some surviving processor retains knowledge of the results.
另外 PAXOS 不要求對(duì)所有節(jié)點(diǎn)做實(shí)時(shí)同步,實(shí)質(zhì)上是考慮到了分區(qū)情況下的可用性,通過(guò)減少完成一次事務(wù)需要的參與者個(gè)數(shù),來(lái)保障系統(tǒng)的可用性。
②OceanBase 的 CAP 分析
上文提到過(guò),單元化架構(gòu)中的成千山萬(wàn)的應(yīng)用就像是計(jì)算器,本身無(wú) CAP 限制,其 CAP 限制下沉到了其數(shù)據(jù)庫(kù)層,也就是螞蟻?zhàn)匝械姆植际綌?shù)據(jù)庫(kù) OceanBase(本節(jié)簡(jiǎn)稱 OB)。
在 OB 體系中,每個(gè)數(shù)據(jù)庫(kù)實(shí)例都具備讀寫(xiě)能力,具體是讀是寫(xiě)可以動(dòng)態(tài)配置(參考第二部分)。
實(shí)際情況下大部分時(shí)候,對(duì)于某一類數(shù)據(jù)(固定用戶號(hào)段的數(shù)據(jù))任意時(shí)刻只有一個(gè)單元會(huì)負(fù)責(zé)寫(xiě)入某個(gè)節(jié)點(diǎn),其他節(jié)點(diǎn)要么是實(shí)時(shí)庫(kù)間同步,要么是異步數(shù)據(jù)同步。
OB 也采用了 PAXOS 共識(shí)協(xié)議。實(shí)時(shí)庫(kù)間同步的節(jié)點(diǎn)(包含自己)個(gè)數(shù)至少需要 (N/2)+1 個(gè),這樣就可以解決分區(qū)容忍性問(wèn)題。
下面我們舉個(gè)馬老師改英文名的例子來(lái)說(shuō)明 OB 設(shè)計(jì)的精妙之處:
假設(shè)數(shù)據(jù)庫(kù)按照用戶 ID 分庫(kù)分表,馬老師的用戶 ID 對(duì)應(yīng)的數(shù)據(jù)段在 [0-9],開(kāi)始由單元 A 負(fù)責(zé)數(shù)據(jù)寫(xiě)入。
假如馬老師(用戶 ID 假設(shè)為 000)正在用支付寶 App 修改自己的英文名,馬老師一開(kāi)始打錯(cuò)了,打成了 Jason Ma,A 單元收到了這個(gè)請(qǐng)求。
這時(shí)候發(fā)生了分區(qū)(比如 A 網(wǎng)絡(luò)斷開(kāi)了),我們將單元 A 對(duì)數(shù)據(jù)段 [0,9] 的寫(xiě)入權(quán)限轉(zhuǎn)交給單元 B(更改映射),馬老師這次寫(xiě)對(duì)了,為 Jack Ma。
而在網(wǎng)絡(luò)斷開(kāi)前請(qǐng)求已經(jīng)進(jìn)入了 A,寫(xiě)權(quán)限轉(zhuǎn)交給單元 B 生效后,A 和 B 同時(shí)對(duì) [0,9] 數(shù)據(jù)段進(jìn)行寫(xiě)入馬老師的英文名。
假如這時(shí)候都允許寫(xiě)入的話就會(huì)出現(xiàn)不一致,A 單元說(shuō)我看到馬老師設(shè)置了 Jason Ma,B 單元說(shuō)我看到馬老師設(shè)置了 Jack Ma。
然而這種情況不會(huì)發(fā)生的,A 提議說(shuō)我建議把馬老師的英文名設(shè)置為 Jason Ma 時(shí),發(fā)現(xiàn)沒(méi)人回應(yīng)它。
因?yàn)槌霈F(xiàn)了分區(qū),其他節(jié)點(diǎn)對(duì)它來(lái)說(shuō)都是不可達(dá)的,所以這個(gè)提議被自動(dòng)丟棄,A 心里也明白是自己分區(qū)了,會(huì)有主分區(qū)替自己完成寫(xiě)入任務(wù)的。
同樣的,B 提出了將馬老師的英文名改成 Jack Ma 后,大部分節(jié)點(diǎn)都響應(yīng)了,所以 B 成功將 Jack Ma 寫(xiě)入了馬老師的賬號(hào)記錄。
假如在寫(xiě)權(quán)限轉(zhuǎn)交給單元 B 后 A 突然恢復(fù)了,也沒(méi)關(guān)系,兩筆寫(xiě)請(qǐng)求同時(shí)要求獲得 (N/2)+1 個(gè)節(jié)點(diǎn)的事務(wù)鎖,通過(guò) no-wait 設(shè)計(jì),在 B 獲得了鎖之后,其他爭(zhēng)搶該鎖的事務(wù)都會(huì)因?yàn)槭《貪L。
下面我們分析下 OB 的 CAP:
分區(qū)容忍性:OB 節(jié)點(diǎn)之間是有互相通信的(需要相互同步數(shù)據(jù)),所以存在分區(qū)問(wèn)題,OB 通過(guò)僅同步到部分節(jié)點(diǎn)來(lái)保證可用性。這一點(diǎn)就說(shuō)明 OB 做了分區(qū)容錯(cuò)。
可用性分區(qū)容忍性:OB 事務(wù)只需要同步到?(N/2)+1 個(gè)節(jié)點(diǎn),允許其余的一小半節(jié)點(diǎn)分區(qū)(宕機(jī)、斷網(wǎng)等),只要 (N/2)+1 個(gè)節(jié)點(diǎn)活著就是可用的。
極端情況下,比如 5 個(gè)節(jié)點(diǎn)分成 3 份(2:2:1),那就確實(shí)不可用了,只是這種情況概率比較低。
一致性分區(qū)容忍性:分區(qū)情況下意味著部分節(jié)點(diǎn)失聯(lián)了,一致性顯然是不滿足的。但通過(guò)共識(shí)算法可以保證當(dāng)下只有一個(gè)值是合法的,并且最終會(huì)通過(guò)節(jié)點(diǎn)間的同步達(dá)到最終一致性。
所以 OB 仍然沒(méi)有逃脫 CAP 魔咒,產(chǎn)生分區(qū)的時(shí)候它變成 AP+最終一致性(C)。整體來(lái)說(shuō),它是 AP 的,即高可用和分區(qū)容忍。
結(jié)語(yǔ)
個(gè)人感覺(jué)本文涉及到的知識(shí)面確實(shí)不少,每個(gè)點(diǎn)單獨(dú)展開(kāi)都可以討論半天。回到我們緊扣的主旨來(lái)看,雙十一海量支付背后技術(shù)上大快人心的設(shè)計(jì)到底是啥?
我想無(wú)非是以下幾點(diǎn):
基于用戶分庫(kù)分表的 RZone 設(shè)計(jì)。每個(gè)用戶群獨(dú)占一個(gè)單元給整個(gè)系統(tǒng)的容量帶來(lái)了爆發(fā)式增長(zhǎng)。
RZone 在網(wǎng)絡(luò)分區(qū)或?yàn)?zāi)備切換時(shí) OB 的防腦裂設(shè)計(jì)(PAXOS)。我們知道 RZone 是單腦的(讀寫(xiě)都在一個(gè)單元對(duì)應(yīng)的庫(kù)),而網(wǎng)絡(luò)分區(qū)或者災(zāi)備時(shí)熱切換過(guò)程中可能會(huì)產(chǎn)生多個(gè)腦,OB 解決了腦裂情況下的共識(shí)問(wèn)題(PAXOS 算法)。
基于 CZone 的本地讀設(shè)計(jì)。這一點(diǎn)保證了很大一部分有著“寫(xiě)讀時(shí)間差”現(xiàn)象的公共數(shù)據(jù)能被高速本地訪問(wèn)。
剩下的那一丟丟不能本地訪問(wèn)只能實(shí)時(shí)訪問(wèn) GZone 的公共配置數(shù)據(jù),也興不起什么風(fēng),作不了什么浪。
比如用戶創(chuàng)建這種 TPS,不會(huì)高到哪里去。再比如對(duì)于實(shí)時(shí)庫(kù)存數(shù)據(jù),可以通過(guò)“頁(yè)面展示查詢走應(yīng)用層緩存”+“實(shí)際下單時(shí)再校驗(yàn)”的方式減少其 GZone 調(diào)用量。
而這就是螞蟻 LDC 的 CRG 架構(gòu),相信 54.4 萬(wàn)筆/秒還遠(yuǎn)沒(méi)到 LDC 的上限,這個(gè)數(shù)字可以做到更高。
當(dāng)然雙 11 海量支付的成功不單單是這么一套設(shè)計(jì)所決定的,還有預(yù)熱削峰等運(yùn)營(yíng)+技術(shù)的手段,以及成百上千的兄弟姐妹共同奮戰(zhàn),特此在這向各位雙 11 留守同學(xué)致敬。
感謝大家的閱讀,文中可能存在不足或遺漏之處,歡迎批評(píng)指正。
參考文獻(xiàn):
Practice of Cloud System Administration, The: DevOps and SRE Practices for Web Services, Volume 2. Thomas A. Limoncelli, Strata R. Chalup, Christina J. Hogan.
MySQL 5.7 半同步復(fù)制技術(shù):https://www.cnblogs.com/zero-gg/p/9057092.html
BASE 理論分析:https://www.jianshu.com/p/f6157118e54b
Keepalived:https://baike.baidu.com/item/Keepalived/10346758?fr=aladdin
PAXOS:https://en.wikipedia.org/wiki/Paxos_(computer_science)
OceanBase 支撐 2135 億成交額背后的技術(shù)原理
https://www.cnblogs.com/antfin/articles/10299396.html
Backup:https://en.wikipedia.org/wiki/Backup
往期推薦
使用 Stream API 高逼格 優(yōu)化 Java 代碼!
Chrome App 將停用
為什么 128 KB 的魂斗羅可以實(shí)現(xiàn)那么長(zhǎng)的劇情?
面試官問(wèn):select......for update會(huì)鎖表還是鎖行?
在 Spring Boot 中,如何干掉 if else
大文件上傳服務(wù)器、支持超大文件HTTP斷點(diǎn)續(xù)傳實(shí)踐總結(jié)
總結(jié)
- 上一篇: [ECharts] DEPRECATED
- 下一篇: 类与对象PK小游戏