淘寶的高可用異地多活架構到底有多牛?

異地多活,作爲一種高可用部署架構,成爲大中型互聯網公司的選擇。像大家熟知的大型互聯網公司,如阿里、騰訊、百度、網易、新浪等等都已經完成了異地多活的技術重構。

可以說,異地多活是互聯網公司業務規模擴大後所必然要經歷的階段。那麼如何解決高可用異地多活呢?

有狀態服務

後臺服務可以劃分爲兩類,有狀態和無狀態。高可用對於無狀態的應用來說是比較簡單的,無狀態的應用,只需要通過 F5 或者任何代理的方式就可以很好的解決。

後文描述的主要是針對有狀態的服務進行分析。服務端進行狀態維護主要是通過磁盤或內存進行保存,比如 MySQL 數據庫,Redis 等內存數據庫。

除了這兩種類型的維護方式,還有 JVM 的內存的狀態維持,但 JVM 的狀態生命週期通常很短。

高可用的一些解決方案

高可用,從發展來看,大致經過了這幾個過程:

冷備

雙機熱備

同城雙活

異地雙活

異地多活

在聊異地多活的時候,還是先看一些其他的方案,這有利於我們理解很多設計的緣由。

冷備

冷備,通過停止數據庫對外服務的能力,通過文件拷貝的方式將數據快速進行備份歸檔的操作方式。

簡而言之,冷備,就是複製粘貼,在 Linux 上通過 cp 命令就可以很快完成。可以通過人爲操作,或者定時腳本進行。

有如下好處:

簡單

快速備份(相對於其他備份方式)

快速恢復。只需要將備份文件拷貝回工作目錄即完成恢復過程(亦或者修改數據庫的配置,直接將備份的目錄修改爲數據庫工作目錄)。更甚,通過兩次 mv 命令就可瞬間完成恢復。

可以按照時間點恢復。比如,幾天前發生的拼多多優惠券漏洞被人刷掉很多錢,可以根據前一個時間點進行還原,“挽回損失”。

以上的好處,對於以前的軟件來說,是很好的方式。但是對於現如今的很多場景,已經不好用了,因爲:

服務需要停機。N 個 9 肯定無法做到了。然後,以前我們的停機冷備是在凌晨沒有人使用的時候進行,但是現在很多的互聯網應用已經是面向全球了,所以,任何時候都是有人在使用的。

數據丟失。如果不採取措施,那麼在完成了數據恢復後,備份時間點到還原時間內的數據會丟失。

傳統的做法,是冷備還原以後,通過數據庫日誌手動恢復數據。比如通過 redo 日誌,更甚者,我還曾經通過業務日誌去手動回放請求恢復數據。恢復是極大的體力活,錯誤率高,恢復時間長。

冷備是全量備份。全量備份會造成磁盤空間浪費,以及容量不足的問題,只能通過將備份拷貝到其他移動設備上解決。

所以,整個備份過程的時間其實更長了。想象一下每天拷貝幾個T的數據到移動硬盤上,需要多少移動硬盤和時間。並且,全量備份是無法定製化的,比如只備份某一些表,是無法做到的。

如何權衡冷備的利弊,是每個業務需要考慮的。

雙機熱備

熱備,和冷備比起來,主要的差別是不用停機,一邊備份一邊提供服務。但還原的時候還是需要停機的。由於我們討論的是和存儲相關的,所以不將共享磁盤的方式看作雙機熱備。

①Active/Standby 模式

相當於 1 主 1 層,主節點對外提供服務,從節點作爲 backup。通過一些手段將數據從主節點同步到從節點,當故障發生時,將從節點設置爲工作節點。數據同步的方式可以是偏軟件層面,也可以是偏硬件層面的。

偏軟件層面的,比如 MySQL 的 master/slave 方式,通過同步 binlog 的方式;sqlserver 的訂閱複製方式。

偏硬件層面,通過扇區和磁盤的攔截等鏡像技術,將數據拷貝到另外的磁盤。偏硬件的方式,也被叫做數據級災備;偏軟件的,被叫做應用級災備。後文談得更多的是應用級災備。

②雙機互備

本質上還是 Active/Standby,只是互爲主從而已。雙機互備並不能工作於同一個業務,只是在服務器角度來看,更好的壓榨了可用的資源。

比如,兩個業務分別有庫 A 和 B,通過兩個機器 P 和 Q 進行部署。那麼對於 A 業務,P 主 Q 從,對於 B 業務,Q 主 P 從。

整體上看起來是兩個機器互爲主備。這種架構下,讀寫分離是很好的,單寫多讀,減少衝突又提高了效率。

其他的高可用方案還可以參考各類數據庫的多種部署模式,比如 MySQL 的主從、雙主多從、MHA;Redis 的主從,哨兵,Cluster 等等。

同城雙活

前面講到的幾種方案,基本都是在一個局域網內進行的。業務發展到後面,有了同城多活的方案。

和前面比起來,不信任的粒度從機器轉爲了機房。這種方案可以解決某個 IDC 機房整體掛電的情況(停電,斷網等)。

同城雙活其實和前文提到的雙機熱備沒有本質的區別,只是“距離”更遠了,基本上還是一樣(同城專線網速還是很快的)。雙機熱備提供了災備能力,雙機互備避免了過多的資源浪費。

在程序代碼的輔助下,有的業務還可以做到真正的雙活,即同一個業務,雙主,同時提供讀寫,只要處理好衝突的問題即可。需要注意的是,並不是所有的業務都能做到。

業界更多采用的是兩地三中心的做法。遠端的備份機房能更大的提供災備能力,能更好的抵抗地震,恐襲等情況。雙活的機器必須部署到同城,距離更遠的城市作爲災備機房。

災備機房是不對外提供服務的,只作爲備份使用,發生故障了才切流量到災備機房;或者是隻作爲數據備份。原因主要在於:距離太遠,網絡延遲太大。

圖 1:兩地三中心

如上圖,用戶流量通過負載均衡,將服務 A 的流量發送到 IDC1,服務器集 A;將服務 B 的流量發送到 IDC2,服務器 B。

同時,服務器集 a 和 b 分別從 A 和 B 進行同城專線的數據同步,並且通過長距離的異地專線往 IDC3 進行同步。

當任何一個 IDC 當機時,將所有流量切到同城的另一個 IDC 機房,完成了failover。

當城市 1 發生大面積故障時,比如發生地震導致 IDC1 和 2 同時停止工作,則數據在 IDC3 得以保全。

同時,如果負載均衡仍然有效,也可以將流量全部轉發到 IDC3 中。不過,此時 IDC3 機房的距離非常遠,網絡延遲變得很嚴重,通常用戶的體驗的會受到嚴重影響的。

圖 2:兩地三中心主從模式

上圖是一種基於 Master-Slave 模式的兩地三中心示意圖。城市 1 中的兩個機房作爲 1 主 1 從,異地機房作爲從。

也可以採用同城雙主+Keepalived+VIP 的方式,或者 MHA 的方式進行failover。但城市 2 不能(最好不要)被選擇爲 Master。

異地雙活

同城雙活可以應對大部分的災備情況,但是碰到大面積停電,或者自然災害的時候,服務依然會中斷。

對上面的兩地三中心進行改造,在異地也部署前端入口節點和應用,在城市 1 停止服務後將流量切到城市 2,可以在降低用戶體驗的情況下,進行降級。但用戶的體驗下降程度非常大。

所以大多數的互聯網公司採用了異地雙活的方案:

圖 3:簡單的異地雙活示意圖

上圖是一個簡單的異地雙活的示意圖。流量經過 LB 後分發到兩個城市的服務器集羣中,服務器集羣只連接本地的數據庫集羣,只有當本地的所有數據庫集羣均不能訪問,才 failover 到異地的數據庫集羣中。

在這種方式下,由於異地網絡問題,雙向同步需要花費更多的時間。更長的同步時間將會導致更加嚴重的吞吐量下降,或者出現數據衝突的情況。

吞吐量和衝突是兩個對立的問題,你需要在其中進行權衡。例如,爲了解決衝突,引入分佈式鎖/分佈式事務。

爲了解決達到更高的吞吐量,利用中間狀態、錯誤重試等手段,達到最終一致性;降低衝突,將數據進行恰當的 sharding,儘可能在一個節點中完成整個事務。

對於一些無法接受最終一致性的業務,餓了麼採用的是下圖的方式:

對於個別一致性要求很高的應用,我們提供了一種強一致的方案(Global Zone),Globa Zone 是一種跨機房的讀寫分離機制,所有的寫操作被定向到一個 Master 機房進行,以保證一致性,讀操作可以在每個機房的 Slave 庫執行,也可以 bind 到 Master 機房進行,這一切都基於我們的數據庫訪問層(DAL)完成,業務基本無感知。

《餓了麼異地多活技術實現(一)總體介紹》

也就是說,在這個區域是不能進行雙活的。採用主從而不是雙寫,自然解決了衝突的問題。

實際上,異地雙活和異地多活已經很像了,雙活的結構更爲簡單,所以在程序架構上不用做過多的考慮,只需要做傳統的限流,failover 等操作即可。

但其實雙活只是一個臨時的步驟,最終的目的是切換到多活。因爲雙活除了有數據衝突上的問題以外,還無法進行橫向擴展。

異地多活

圖 4:異地多活的示意圖

根據異地雙活的思路,我們可以畫出異地多活的一種示意圖。每個節點的出度和入度都是 4,在這種情況下,任何節點下線都不會對業務有影響。

但是,考慮到距離的問題,一次寫操作將帶來更大的時間開銷。時間開銷除了影響用戶體驗以外,還帶來了更多的數據衝突。

在嚴重的數據衝突下,使用分佈式鎖的代價也更大。這將導致系統的複雜度上升,吞吐量下降。所以上圖的方案是無法使用的。

回憶一下我們在解決網狀網絡拓撲的時候是怎麼優化的?引入中間節點,將網狀改爲星狀:

圖 5:星狀的異地多活

改造爲上圖後,每個城市下線都不會對數據造成影響。對於原有請求城市的流量,會被重新 LoadBalance 到新的節點(最好是 LB 到最近的城市)。

爲了解決數據安全的問題,我們只需要針對中心節點進行處理即可。但是這樣,對於中心城市的要求,比其他城市會更高。

比如恢復速度,備份完整性等,這裏暫時不展開。我們先假定中心是完全安全的。

如果我們已經將異地多活的業務部署爲上圖的結構,很大程度解決了數據到處同步的問題,不過依然會存在大量的衝突,衝突的情況可以簡單認爲和雙活差不多。那麼還有沒有更好的方式呢?

回顧一下前文提到的餓了麼的 GlobalZone 方案,總體思路就是“去分佈式”,也就是說將寫的業務放到一個節點的(同城)機器上。

阿里是這麼思考的:

阿里理想中的異地多活架構

實際上我猜測很多業務也是按照上圖去實現的,比如滴滴打車業務這種,所有的業務都是按城市劃分開的。

用戶、車主、目的地,他們的經緯度通常都是在同一個城市的。單個數據中心並不需要和其他數據中心進行數據交互,只有在統計出報表的時候才需要,但報表是不太注重實時性的。

那麼,在這種情況下,全國的業務其實可以被很好的 sharding 的。但是對於電商這種複雜的場景和業務,按照前文說的方式進行 sharding 已經無法滿足需求了。

因爲業務線非常複雜,數據依賴也非常複雜,每個數據中心相互進行數據同步的情況無可避免。

淘寶的解決方式和我們切分微服務的方式有點類似:

淘寶按照單元切分的異地多活架構

注意看圖中的數據同步箭頭。以交易單元爲例,屬於交易單元的業務數據,將與中心單元進行雙向同步;不屬於交易單元的業務數據,單向從中心單元同步。

中心單元承擔了最複雜的業務場景,業務單元承擔了相對單一的場景。對於業務單元,可以進行彈性伸縮和容災。

對於中心單元,擴展能力較差,穩定性要求更高。可以預見,大部分的故障都會出現在中心單元。

按照業務進行單元切分,已經需要對代碼和架構進行徹底地改造了(可能這也是爲什麼阿里要先從雙活再切到多活,歷時3年)。比如,業務拆分,依賴拆分,網狀改星狀,分佈式事務,緩存失效等。

除了對於編碼的要求很高以外,對測試和運維也有非常大的挑戰。如此複雜的情況,如何進行自動化覆蓋,如何進行演練,如何改造流水線。這種級別的災備,不是一般公司敢做的,投入產出也不成正比。

不過還是可以把這種場景當作我們的“假想敵”,去思考我們自己的業務,未來會怎麼發展,需要做到什麼級別的災備。相對而言,餓了麼的多活方案可能更適合大多數的企業。

本文只是通過畫圖的方式進行了簡單的描述,其實異地多活是需要很多很強大的基礎能力的。

比如,數據傳輸,數據校驗,數據操作層(簡化客戶端控制寫和同步的過程)等。

思考

最後,留幾個問題大家可以思考一下:

假設你在做餓了麼的開發,服務按照異地多活方式部署,sharding key 根據省市區進行分片。假設買家在多個城市交匯的地方,比如,十字路口的四個位置分別是 4 個城市,那麼如何處理才能讓他拉到比較正常的數據?

你們現在的業務模塊中,哪些業務是可以做多活的,哪些無法做多活?

所有的業務都要做多活嗎?還是隻需要核心業務做多活?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章