無感平滑遷移:海量高併發數據庫如何進行國產化改造?

作者簡介:鄭鴻健,中國移動通信集團信息技術公司政企業務支撐中心,數據庫專家,主要負責政企業務支撐中心數據層高性能、高可靠架構的設計、優化以及國產化的落地。

歷任騰訊 DBA、樂信數據 SRE 負責人、業務 SRE 中心總監,有豐富的數據庫、大數據、中間件及業務運維經驗,主導過多種業務場景下高性能、高可用架構設計、業務全鏈路容災體系建設、SRE 體系落地等大型項目。

今天分享的主題是 “無感平滑遷移:海量高併發數據庫國產化改造及應用”。

首先,講一下數據庫國產化的大背景。

一、數據庫國產化的背景

國家戰略方面的,隨着外部形勢的日益複雜,核心技術急需實現自主可控、安全可靠、高效開放;另一個要求是業務方面的,當業務高速發展後各種問題會接踵而至,單機數據庫達到瓶頸,業務拆分、垂直拆分、水平拆分等,都需要花費大量的研發時間。

二、主流的數據庫架構

首先和大家分享一下目前主流的數據庫架構。目前業內主流的數據庫架構大體分爲三類:Shared-Everything、Shared-Nothing,Shared-Storage。

這一種架構可能大家都比較熟悉,是很經典的一個架構,主機上的所有進程共享 CPU、內存、IO,任意一個硬件達到了瓶頸,也就意味着數據庫達到了瓶頸。

這裏可以再細分出兩個架構,一種是基於 Proxy 的架構,這個架構由傳統的單機數據庫演變而來,當我們單機數據庫達到瓶頸以後,我們再往上加一層 Proxy,通過 Proxy 把我們的數據打散到不同的節點上,以此來解決數據擴展性問題;另一種 Shared-Nothing 架構,是目前在國內比較火的像 TiDB、OB 這樣的數據庫。

這種架構在業內比較有名的是的 Aurora,還有目前在國內也比較火的阿里的 PolarDB。

首先,先詳細介紹一下 Shared-Everything 架構。這個架構可能大家都比較熟悉,它是一種傳統的單機數據庫架構,當我們的數據庫達到瓶頸以後,我們通常會採用各種各樣的方式把我們的數據打散到各個 set 裏面去,以此來解決數據庫當地容量上限的問題。

有很多公司會通過這種方式來實現,把應用層、接入層、網關層都根據同樣的分片邏輯,把數據整合到一個 set 裏面,從而實現數據在 set 裏面的閉環。通過這樣的一套架構能做非常多有意思的事情。比如一些全鏈路在線的壓測,因爲數據已經在一個 set 裏面不閉環了,如果我在一個測試 set 裏面做數據壓測,它不會污染到線上真實的數據,還可以做一些向線上的灰度引流、灰度發版等。

Shared-Storage 架構中,目前在國外做得特別好的是 AWS 的 Aurora,在國內做得比較好的是阿里的 PolarDB。因爲它對數據庫研發投入非常高,同時需要依託底層一個非常強大的底座,所以一開始這些數據庫推出來之後,像 Aurora 至今都是隻支持在雲上部署。

Shared-Nothing 架構,大家已經比較熟悉了,我們傳統的數據庫達到瓶頸演進後,單機解決不了性能、容量問題,於是就加一層 Proxy,在 Proxy 上做各種各樣的路由,這樣的架構慢慢地演進到現在。國內有非常多基於這樣的架構去演化出來的數據庫,目前移動用的比較多的數據庫都是類似的架構。

這套架構主要可以分成三類組件:

GTM 組件負責全局的協調,主要用來取協調分佈式事務的管理,包括全局 ID 的生成活躍,GTE ID 的快照等等。

Proxy / 計算節點。一開始 Proxy 這一層可能只是簡單做數據的路由,但是後面隨着整個數據庫架構的演進,Proxy 這一層也承擔起一定的計算能力,包括分佈式事務的優化、計算的下推,還有一些 SQL 解析等。

底層的存儲節點,大多數目前都是基於開源的數據庫去做二次開發,目前比較常見的就是 MySQL 和 PostgreSQL。它的架構的好處就是比較成熟穩定。但是它的壞處就是整個改造的過程非常痛苦,因爲我們在整個改造的過程中,第一步需要去選取分片鍵,然後根據我們的分片鍵把數據打散到不同的節點上去,這一步就足以讓業務抓狂。

Shared-Nothing 的另外一種架構是剛剛提到的 OB、TiDB。這裏以 TiDB 爲例, TiDB 是基於谷歌的論文去開發的一套分佈式數據庫。這個數據庫它分成三層,調度層 PD,計算節點 TiDB,然後是底層的存儲節點 TiKV。

但是 TiDB 它在使用的過程中不需要顯式地去指定分片鍵,它數據的分片拆分都是與依託於底下的數據庫自動去完成的,所以採用這樣的架構的話,改造成本可能會相對於低一些。

小結,以上提到的四種數據庫架構,可以看到它們在擴展性層面,除了第一種沒辦法做到動態橫向擴容,後面的三種其實只要改造完了,基本上都能做到數據層的橫向擴容。

在一致性方面,第一種和最後一種基本上都是依託於半同步去保證各個節點之間的一致性。中間這兩類數據庫一般都是用了分佈式一致性協議,目前用的比較多的是 Paxio 協議,還有的 Raft 協議。

下面,跟大家分享我們在數據庫國產化挑戰與探索上的一些實踐經驗。

三、數據庫國產化挑戰與探索

業務邏輯和數據層的深度綁定是我們整個數據庫改造過程中最頭疼的一個點,於是出現了各種各樣的數據庫遷移方案,總結下來無非是以下七步:選型、測試、同步、改造、灰度、上線、保障。

首先是選型階段,我們關注更多的是穩定性、效率、成本以及生態。選型階段是一個很重要的階段,因爲如果選型選得好,可以極大地降低我們在後面數據庫國產化改造的過程中的成本。但是很多時候我們在做數據庫改造的過程中,選型階段除了考慮技術的因素外,還有很多非技術的因素需要去考慮,所以最後我們只能在有限的範圍內選出最優解。不能指望在選型階段就把整個數據庫國產化改造中的所有問題都一併解決掉。

緊接着是測試階段,我們拿到一塊數據庫之後,可能會做各種各樣的測試,比如基礎功能測試、可用性測試、可維護性測試,還有一些基礎的性能測試等。但是哪怕我們做了這樣的測試以後,也不能保證這套數據庫可以滿足業務的需求,更多的是我們在做完這樣的一些測試之後,淘汰一些不滿足我們需求的一些數據庫。如果我們想篩選出哪些數據庫能基本符合我們的要求,可能還要結合一些線上的流量錄製以及流量回放的一些東西。

數據同步這裏可以稍微展開講一下,這一塊主要是分爲全量數據同步增量數據同步,我強烈建議大家在做這種大數據量的遷移時候,不要上來就去研究全備要用物理備份還是邏輯備份,哪個工具強,哪個工具差。第一步更多的是需要去分析一下自己的業務,分析自己的庫表、業務邏輯是怎樣的、數據到底是怎麼分佈的,哪些是歷史數據、熱數據、冷數據,哪些數據是可以現在立馬要遷移的、哪些數據是可以放到放到後面慢慢遷的…… 在做完這樣的數據分析之後,再來做全量和增量的數據遷移,往往可以做到事半功倍****。

在增量同步這一塊,我們內部也開發了類似的一個增量數據的同步工具。它是去監聽了數據庫變化的日誌,然後把這些變化通過通過中間件的方式寫到像 Kafka 這樣的消息中間件,然後有需求的業務再去定從我們的中間件裏面去訂閱。

通過這樣的一個工具能做非常多有意思的事情。像我們剛剛提到的數據遷移,那麼可以通過這些工具的來做異構數據的同步,還可以做一些像數據庫當緩存之間的同步,或者是說 OLTP 向 OlAP 之間的同步等等。通過這個工具能解決非常多的業務的痛點。

業務改造這一塊也是非常痛苦的一個點。我們改造的關鍵點主要分兩塊,一塊是應用,應用這一塊更多的是關注一些驅動程序、語法兼容、數據對象等等、API、 SQL 等等的一些東西。數據庫這一層更多的是關注一些像數據分片、冷熱分離、輕重隔離、SQL 優化、讀寫分離等等這一些的東西。

這個適配改造我們目前改造的積累下來的需要關注的點,已經到大幾十項了,所以這裏我也沒有詳細地列出來,只是稍微地把它彙總了一下。我剛剛在前面幾頁提到的就是數據庫國產化改造中最大的一個痛點就是業務邏輯和數據庫的深度耦合,把這個問題給解決掉。所以我們在改造的過程中逐步地把那個 DB 給弱化成一個簡單的存儲,然後明確應用和數據之間的一個邊界,然後再去做各種各樣的適配改造。

通過這一系列操作把數據庫國產化的這件事情,也作爲我們整一個架構的梳理和優化。切換方案這裏可以稍微簡單地提一下。我們的切換方案主要有兩種,目前這兩種方案都有在用:第一種是基於數據層的中間件的方案,這種方案相對而言會比較簡單一些,另一種方案是基於應用的雙寫方案,這種方案可能相對比較複雜,但是這種穩定性和安全性要求會對穩定性和要求的保障會更好一些。


我先簡單介紹一下基於數據層的切換,一開始我們的應用可能都是直接連到我們的 DB,中間有一些 VIP 的東西,我直接給它省略掉。開始我們給它加了一層中間件,從第一步到第二步之間,因爲中間件指向的也是我們的這個 DB,所以研發可以慢慢地去改。這個改造的過程耗費十天半個月都沒有關係,這它對業務是不會有任何的影響的。當我們把所有的流量都切到中間件以後,我們在數據庫這一層去抓包,確保我們的那個請求全部都是通過中間件來的。

等到應用全部改造完成了之後,我們把數據全量地同步到新的數據庫去,然後可以把我們的讀流量放到新數據庫上。因爲應用已經通過我們的中間件過來的,所以我們在中間件上做讀寫分離,對到應用來說是完全透明的。

我們把我們的讀流量打到我們的新的數據庫上來,以此去驗證新的數據庫是否能夠滿足業務的要求,當然通過這樣的架構其實是沒有辦法做到寫流量的驗證的。在切換過程,對業務的影響就只是那麼零點幾秒的閃斷,替換完之後我們再把數據往回同步,如果真的出現了問題,我們還有一個退路可以往回退。

第二種方案是業務雙寫的方案,這一個方案可能會相對於上一個方案來說更穩妥,但是也更復雜一些。首先我們準備一套新的數據庫,先做全量同步,再做增量同步,保證兩邊的數據是追平的。然後我們再找一個時間窗口,當然前提是我們就上一步已經執行完了,就是我們應用的適配卡照已經執行完了,然後通過我們的應用去開啓我們的雙寫,然後保證兩邊的數據都寫一致,這個時候我再去把增量同步給停掉,然後底下再開啓一個腳本,異步地去對比兩邊的數據,保證兩邊的數據是一致的。

對賬的過程中如果發現不一致的東西,就打日誌出來,然後人工分析,當保證兩邊是一致以後,就可以開始去分析一下。因爲這個時候我們的業務是以老庫爲主,新庫爲輔,這個時候新庫它是既有讀又有寫的,我們可以從業務上去觀察到新庫的各種解指標,包括耗時、錯誤率等等,如果新庫是有問題的,在這個階段基本上都能發現,而且對業務是毫無影響的。足夠穩定之後我們再進行雙寫扭轉,把新庫爲主,舊庫爲輔,然後這樣再持續穩運行一段時間。當我們發現用新的庫足夠穩定之後,再把雙寫給下掉。

做完了上面那一步,就到了上線後的保障階段。這裏主要分兩塊來做,一塊是可觀致性,另外一塊是可控性、可觀測性,保證了我們在出現故障的時候能夠快速地發現,快速地定位,可控性保證了我們在出現故障之後可以快速地響應以及快速地恢復。

可觀測性這一塊 loging、 tracing、metric 這三個大家都經常聽到,就不細說了。在我們內部其實也針對剛剛的那幾個點做了對應的建設,包括底層資源的監控,業務層的監控、日誌採集平臺、調用鏈路分析平臺。在可恢復這一塊,其實我們通過過往的故障可以發現就是故障的時候,故障恢復的時間大多數並不是花在就是故障的修復的那一刻,時間更多的是花在信息的溝通人員的分工上,就從故障的出現到協調到對應的人,然後再把信對應的信息同步,再到決策要做怎麼樣的操作。

我們對時間分佈做了一整套應急響應體系。我們把故障分爲輕微問題、中等問題、還有重大問題,以此打造了一個預案管理平臺,平時運維可能更多的只需要在上面建立我們的原子化的一些預案能力。舉個例子,SQL 的 kill,數據庫的主從切換等等這樣的一些原子能力。然後我們把這樣各種各樣的非常成熟的原子能力整合到那個預案平臺,然後在預案平臺上做各種各樣的故障預案的編排,結合 AI,在出現故障的時候,自動地去推薦預案再輔以人爲的判斷。

通過把這樣的一些智能化的預案推送到我們的移動端,那麼人可能需要做的事情只是在移動端上點一點,那麼它就可以自動化地去執行我們的預案。像遇到一些在改造過程中遇到的比較簡單的問題,就執行降級操作,降級包括業務降級、請求降級、底層的資源降級,技術層面可能我們還會做一些架構的降級。

最後我們還有我們的兜底方案,前面提到的切換方案中,不管是我們的雙寫方案,還是我們基於數據庫中間件切換的方案,都能保證我們在秒級內做那個數據庫的回退。像雙寫方案,因爲我們把那個雙寫作爲做一個配置項存的一個開關裏面,我們做回退只需要修改一下那個數據庫切換的一個開關,像中間件的方案,我們只需要切換一下後端的指向。

最後,對我們在數據庫國產化改造的經驗做個小結。

總結

第一是**合適就好。**其實大多數時候,集中式的數據庫仍然是當前的一個最優解,如果你的數據可能只有幾百 G,甚至可能就兩三個 T,那麼集中式數據庫可能仍是當前最好的選擇,因爲你不需要去承擔數據分片,還有這種各種的各樣跨節點同步的耗時這樣一系列的問題。

第二是**沒有銀彈,**但不要指望用一種數據庫去解決所有的問題。過去我們在用 Oracle 的時候,然後 Oracle 承擔了太多不應該他承擔的東西了。現在我們在做的數據庫改造的過程中,慢慢的我們也在解決這樣的一個問題,像該扛量的我們用 Redis,像即席查詢,那麼用 clickhouse 這樣的一些數據庫去扛,然後做一些大的一些報表分析的這樣的一些東西。可能我們會通過我們的同步工具同步到我們的大數據平臺,通過大數據平臺去做這樣的一些分析。

第三是拆掉運維裏的牆,打破技術和業務的壁壘。技術只有服務於業務、和業務結合,而運維只有跳出自己的框框,站到全局的視角去看待問題,那麼才能真正做到的事半功倍,才能稱之爲真正的技術運營。

以上是我這次的分享,非常感謝大家。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/42vhTW1LSsQMnXTIPQOp-A