天天看點

高可用架構:異地多活1.為什麼要做多活2.架構演變

前言:多「活」、多「備」是兩個相對的概念,設計和實作的難度相差很大,不要搞混了

1.為什麼要做多活

        在一些極端場景下,有可能所有伺服器都出現故障,例如機房斷電、機房火災、地震等這些不卡抗拒因素會導緻系統所有伺服器都故障進而導緻業務整體癱瘓,而且即使有其他地區的備份,把備份業務系統全部恢複到能夠正常提供業務,花費的時間也比較長。為了滿足中心業務連續性,增強抗風險能力,多活作為一種可靠的高可用部署架構,成為各大網際網路公司的首要選擇。

1.1.多活場景

  • 多活架構的關鍵點就是指不同地理位置上的系統都能夠提供業務服務,這裡的“活”是指實時提供服務的意思。
  • 與“活”對應的是字是“備”,備是備份,正常情況下對外是不提供服務的,如果需要提供服務,則需要大量的人工幹預和操作,花費大量的時間才能讓“備”變成“活。

        單純從描述來看多活很強大,能夠保證在災難的情況下業務都不受影響,是不是意味着不管什麼業務,我們都要去實作多活架構呢?其實不是,實作多活架構都要付出一定的代價,具體表現為:

  1. 不同多活方案實作複雜度不一樣,随着業務規模和容災級别的提升,多活方案會給業務系統設計帶來更大複雜度
  2. 不管采用哪種多活方案都難以完全避免跨機房甚至是跨地區服務調用帶來的耗時增加
  3. 多活會帶來成本會上升,畢竟要多在一個或者多個機房搭建獨立的一套業務系統

        是以,多活雖然功能很強大,但也不是每個業務都要上多活。例如,企業内部的 IT 系統、管理系統、部落格站點等,如果無法承受異地多活帶來的複雜度和成本,是可以不做異地多活的,而對于重要的業務例如核心金融、支付、交易等有必要做多活。

1.2.多活方案

        常見的多活方案有同城雙活、兩地三中心、三地五中心、異地多活等多種技術方案,不同多活方案技術要求、建設成本、運維成本都不一樣

2.架構演變

2.1.單機架構

2.1.1.最簡單的單機:單庫

先從簡單的講起,假設業務處于起步階段,體量非常小,那架構是這樣的:

高可用架構:異地多活1.為什麼要做多活2.架構演變

這個架構模型非常簡單,用戶端請求進來,業務應用讀寫資料庫,傳回結果,非常好了解。

但需要注意的是,這裡的資料庫是「單機」部署的,是以它有一個緻命的缺點:一旦遭遇意外,例如磁盤損壞、作業系統異常、誤删資料,那這意味着所有資料就全部「丢失」了,這個損失是巨大的。

2.1.2.存儲「冷備份」

如何避免這個問題呢?我們很容易想到一個方案:備份。

高可用架構:異地多活1.為什麼要做多活2.架構演變

你可以對資料做備份,把資料庫檔案「定期」cp 到另一台機器上,這樣,即使原機器丢失資料,你依舊可以通過備份把資料「恢複」回來,以此保證資料安全。

這個方案實施起來雖然比較簡單,但存在 2 個問題:

  1. 恢複需要時間:業務需先停機,再恢複資料,停機時間取決于恢複的速度,恢複期間服務「不可用」
  2. 資料不完整:因為是定期備份,資料肯定不是「最新」的,資料完整程度取決于備份的周期

很明顯,你的資料庫越大,意味故障恢複時間越久。那按照前面我們提到的「高可用」标準,這個方案可能連 1 個 9 都達不到,遠遠無法滿足我們對可用性的要求。

那有什麼更好的方案,既可以快速恢複業務?還能盡可能保證資料完整性呢?

這時你可以采用這個方案:主從副本。

2.2.主從副本

2.2.1.主從複制:存儲「熱備份」

你可以在另一台機器上,再部署一個資料庫執行個體,讓這個新執行個體成為原執行個體的「副本」,讓兩者保持「實時同步」,就像這樣:

高可用架構:異地多活1.為什麼要做多活2.架構演變

我們一般把原執行個體叫作主庫(master),新執行個體叫作從庫(slave)。這個方案的優點在于:

  • 資料完整性高:主從副本實時同步,資料「差異」很小
  • 抗故障能力提升:主庫有任何異常,從庫可随時「切換」為主庫,繼續提供服務
  • 讀性能提升:業務應用可直接讀從庫,分擔主庫「壓力」讀壓力

這個方案不錯,不僅大大提高了資料庫的可用性,還提升了系統的讀性能。

2.2.2.應用部署在其他機器

同樣的思路,你的「業務應用」也可以在其它機器部署一份,避免單點。因為業務應用通常是「無狀态」的(不像資料庫那樣存儲資料),是以直接部署即可,非常簡單。

注意:主庫提供讀/寫,從庫隻讀
高可用架構:異地多活1.為什麼要做多活2.架構演變

2.2.2.部署接入層,實作負載均衡

因為業務應用部署了多個,是以你現在還需要部署一個「接入層」,來做請求的「負載均衡」(一般會使用 nginx 或 LVS),這樣當一台機器當機後,另一台機器也可以「接管」所有流量,持續提供服務。

高可用架構:異地多活1.為什麼要做多活2.架構演變

上面的方案,還是存在風險:對于機房級别的故障是無法避免的

2.3.同城災備

        同城災備分為「冷備」和「熱備」,冷備隻備份資料,不提供服務,熱備實時同步資料,并做好随時切換的準備

想要抵禦「機房」級别的風險,那應對方案就不能局限在一個機房内了。

機房級别的備援方案

  1. A、B兩個機房采用「同城專線」連接配接
  2. 存儲「熱備份」
  3. 備份B機房,提前部署好接入層、應用層,等待随時切換

補充說明:

  • 兩個機房唯一的差別是,A 機房的存儲都是主庫,而 B 機房都是從庫
  • 熱的意思是指,B 機房處于「待命」狀态,A 故障後 B 可以随時「接管」流量,繼續提供服務。熱備相比于冷備最大的優點是:随時可切換。
高可用架構:異地多活1.為什麼要做多活2.架構演變

這樣的話,A 機房整個挂掉,隻需要做 2 件事即可:

  1. B 機房所有從庫提升為主庫
  2. DNS 指向 B 機房接入層,接入流量,業務恢複

同城災備的最大優勢在于,再也不用擔心「機房」級别的故障了,一個機房發生風險,隻需把流量切換到另一個機房即可,可用性再次提高。

2.3.同城雙活

        同城雙活比災備的優勢在于,兩個機房都可以接入「讀寫」流量,提高可用性的同時,還提升了系統性能。雖然實體上是兩個機房,但「邏輯」上還是當做一個機房來用。

同城雙活與上面同城災備的不同點是:同城雙活的備份B機房,接入了外部的流量,提供服務。

補充說明:

        因為 A 機房的存儲都是主庫,是以我們把 A 機房叫做「主機房」,B 機房叫「從機房」。B機房是「從機房」,隻對外提供讀服務。B機房的寫操作要轉到A機房(是以,需要在業務上進行改造,對于存儲的操作,要區分讀寫請求)

高可用架構:異地多活1.為什麼要做多活2.架構演變

雖然把 2 個機房當做一個整體來規劃,但這 2 個機房在實體層面上,還是處于「一個城市」内,如果是整個城市發生自然災害,例如地震、水災(河南水災剛過去不久),那 2 個機房依舊存在「全局覆沒」的風險。

但這次備援機房,就不能部署在同一個城市了,你需要把它放到距離更遠的地方,部署在「異地」。

2.4.兩地三中心

        兩地三中心是在同城雙活的基礎上,額外部署一個異地機房做「災備」,用來抵禦「城市」級别的災害,但啟用災備機房需要時間。

兩地三中心 = 同城雙活、異地災備

  • 兩地:2個城市
  • 三中心:3個機房
  • 2個機房在同一個城市,同時提供服務
  • 第3個機房部署在異地,隻做資料災備
補充說明:“兩地”帶來最大的難題是「網絡延遲」,是以,異地的應用不要“跨網絡”去通路異地的存儲
高可用架構:異地多活1.為什麼要做多活2.架構演變

2.5.異地雙活

        異地雙活才是抵禦「城市」級别災害的更好方案,兩個機房同時提供服務,故障随時可切換,可用性高。但實作也最複雜,了解了異地雙活,才能徹底了解異地多活

Q:先提個問題,異地雙活是不是直接「照搬」同城雙活的模式去部署就可以了呢?

A:不行的,“異地”會帶來最大難題「網絡延遲」。一次跨機房通路延遲就達到了 30ms,這大緻是機房内網網絡(0.5 ms)通路速度的 60 倍(30ms / 0.5ms),一次請求慢 60 倍,來回往返就要慢 100 倍以上

  • 同城雙活可以采用「同城專線」,B機房寫A機房的存儲是很快的
  • 異城雙活即使采用「跨城專線」,B機房寫A機房的存儲還是要跨越地區,延時是無法被業務接收的

是以,引出一個原則:在A城市的機房,不允許讀寫B城市的機房的存儲

2.5.1.初始架構

架構介紹

  1. 兩個機房,都部署了接入層、應用層、存儲層
  2. 由DNS實作流量的轉發,兩個機房的應用可以同時對外提供服務
  3. 兩個機房的存儲必須都是「主庫」,而且兩個機房的資料還要「互相同步」資料,即用戶端無論寫哪一個機房,都能把這條資料同步到另一個機房。
高可用架構:異地多活1.為什麼要做多活2.架構演變

難點1:怎麼實作這種「雙主」架構呢?它們之間如何互相同步資料?

A:對于存儲中間件,要開發對應的「資料同步中間件」來實作雙向同步的功能。業界也開源出了很多資料同步中間件,例如阿裡的 Canal、RedisShake、MongoShake,可分别在兩個機房同步 MySQL、Redis、MongoDB 資料。

難點2:兩個機房都可以寫,如果修改的是同一條的資料,發生沖突怎麼辦?這是一個很嚴重的問題,系統發生故障并不可怕,可怕的是資料發生「錯誤」,因為修正資料的成本太高了。我們一定要避免這種情況的發生。

A:解決這個問題,有 2 個方案

方案1:資料同步中間件要有自動「合并」資料、解決「沖突」的能力

        這個方案實作起來比較複雜,要想合并資料,就必須要區分出「先後」順序。我們很容易想到的方案,就是以「時間」為标尺,以「後到達」的請求為準。

        但這種方案需要兩個機房的「時鐘」嚴格保持一緻才行,否則很容易出現問題。完全「依賴」時鐘的沖突解決方案,不太嚴謹。

方案2:從「源頭」就避免資料沖突的發生,即「在最上層接入流量時,就不要讓沖突的情況發生」

2.5.2.路由層「分片」

避免資料沖突的解決方案:「在最上層接入流量時,就不要讓沖突的情況發生」

        具體來講就是,要在最上層就把使用者「區分」開,部分使用者請求固定打到北京機房,其它使用者請求固定打到上海機房,進入某個機房的使用者請求,之後的所有業務操作,都在這一個機房内完成,從根源上避免「跨機房」。

        是以這時,你需要在接入層之上,再部署一個「路由層」(通常部署在雲伺服器上),自己可以配置路由規則,把使用者「分流」到不同的機房内。

高可用架構:異地多活1.為什麼要做多活2.架構演變

但這個路由規則,具體怎麼定呢?有很多種實作方式,最常見的總結了 3 類:

1.按地理位置分片

        非常适合與地理位置密切相關的業務,例如打車、外賣服務就非常适合這種方案。

        拿外賣服務舉例,你要點外賣肯定是「就近」點餐,整個業務範圍相關的有商家、使用者、騎手,它們都是在相同的地理位置内的。舉例:北京、河北地區的使用者點餐,請求隻會打到北京機房,而上海、浙江地區的使用者,請求則隻會打到上海機房。這樣的分片規則,也能避免資料沖突。

2.按業務類型分片

        假設一共有 4 個應用,北京和上海機房都部署這些應用。但應用 1、2 隻在北京機房接入流量,在上海機房隻是熱備。應用 3、4 隻在上海機房接入流量,在北京機房是熱備。

        這樣一來,應用 1、2 的所有業務請求,隻讀寫北京機房存儲,應用 3、4 的所有請求,隻會讀寫上海機房存儲。

2.直接哈希分片

        會根據使用者 ID 計算「哈希」取模,然後從路由表中找到對應的機房,之後把請求轉發到指定機房内。

        舉例:一共 200 個使用者,根據使用者 ID 計算哈希值,然後根據路由規則,把使用者 1 - 100 路由到北京機房,101 - 200 使用者路由到上海機房,這樣,就避免了同一個使用者修改同一條資料的情況發生。

        分片的核心思路在于,讓同一個使用者的相關請求,隻在一個機房内完成所有業務「閉環」,不再出現「跨機房」通路。阿裡在實施這種方案時,給它起了個名字,叫做「單元化」。

2.5.3.全局資料

        這裡還有一種情況,是無法做資料分片的:全局資料。例如系統配置、商品庫存這類需要強一緻的資料,這類服務依舊隻能采用寫主機房,讀從機房的方案,不做雙活。

        雙活的重點,是要優先保證「核心」業務先實作雙活,并不是「全部」業務實作雙活。

總結:到這裡你可以看出,完成這樣一套架構,需要投入的成本是巨大的。

        路由規則、路由轉發、資料同步中間件、資料校驗兜底政策,不僅需要開發強大的中間件,同時還要業務配合改造(業務邊界劃分、依賴拆分)等一些列工作,沒有足夠的人力物力,這套架構很難實施。

參考:

搞懂異地多活,看這篇就夠了 | Kaito's Blog​​​​​​mysql - 同城雙活與異地多活架構分析 - vivo 網際網路技術 - SegmentFault 思否

繼續閱讀