實戰篇:單庫單表變更成多庫多表

七淅在學Java發表於2022-04-10

大家好,我是七淅(xī)。

如標題所說,本文會結合我自己的親身經歷,介紹 3 部分內容:

  1. 線上單庫單表變更到多庫多表的各個實現方案
  2. 方案優劣對比
  3. 對於歷史存在的單表,並且它們不需要變成多表,需要怎麼處理

先下個結論,沒有百分百完美的方案,技術方案永遠要結合產品業務來設計

以下舉例的方案也只是較為通用的做法,具體細節是可以根據業務場景進行變化調整的。

只要能夠滿足業務需求,就是好方案,不要為了秀技術而忽略業務。

看完這篇文章,如果後面有人問你,關於變更到多庫多表的方案問題,那你可以和他談笑風生了。

好了,下面我說下我這邊的業務背景,和大家解釋清楚為什麼需要多庫多表。後面會引申出方案的,莫急。

1. 業務背景

有一個線上上執行著的資料庫,假設是 user 庫,庫中只有 1 張單表。

現在有個新需求,該需求的功能有一定的請求量和資料量。

其中資料量初期是百萬級,考慮到業務增加,增長到千萬、上億都是有可能的。所以從資料量上看,單庫單表不合適

Q1:如果只是資料量問題,那用單庫多表行不行?
A1:行。
Q2:那為什麼還用多庫多表呢?
A2:因為一個資料庫的連線數量是有限的,怕翻車

上面有介紹業務有一定的請求量,擔心一個庫來處理的話,萬一哪天網路不好/慢查/該表業務有突發性活動等情況出現。

一不小心就把連線數佔滿了,那就直接翻車了。

加上我司對多庫多表的基建比較成熟,所以我這邊就直接上多庫多表了。

Q3:既然如此,前期先上單庫多表,等量上來後再多庫多表行不行?
A3:可以。但是到時再來一次太累了。

比如再來一次會經歷以下事情:

  • 每天需要看看資料監控
  • 有沒有到瓶頸
  • 到時再次變更時,開發運維測試業務的排期和執行
  • 業務變動:說好的下個季度大推,結果提前到下一個月進行,此時資料庫能不能扛住,扛不住改造時間是否充足?

所以,我們要不還是一步到位吧。

滴,七淅提醒你:看到這,如果有人問你單庫多表和多庫多表的使用場景,你應該知道怎麼發揮了吧

2. 歷史資料處理

我先說下對歷史資料處理,篇幅較少。

這裡的內容對應文章開頭的第三點:對於歷史存在的單表,並且它們不需要變成多表,需要怎麼處理

這裡可以有兩種處理方式。

我們知道,歷史資料在 user 庫,假設業務需要增加到 8 個庫,並且新表需要在這 8 個庫中

2.1 方式一

新增 user_0、user_1、...、user_7 共 8 個庫,使用 rename 命令,將 user 庫的表遷移到 user_0 庫中,最後將 user 庫刪掉即可。

rename 命令其實就是重新命名,實現剪下資料的效果,而不是複製。當然要用複製的方式遷移資料也是可以的,但我們這邊沒用。

reanme 命令使用如下:

rename table user.table_name to user_0.table_name;

2.2 方式二

新增 user_0、user_1、...、user_7 共 8 個庫,user 庫資料不動,繼續使用。

至於選哪種方式,大多情況下,我個人認為都可以,但如果歷史表本身請求就很高,那可以考慮用方式二,避免 0 號庫壓力太大。

我這邊是選擇的方式一。當使用者要訪問歷史表時,指定路由到 0 號庫就好了,順便省下一臺資料庫的錢,真香

3. 變更方案

方案這塊內容,我會基於方式一的歷史資料處理方式來講。

首先,先不考慮任何方案,我把最簡單的,變更到多庫多表的操作按順序列舉一下:

  1. 修改服務連線資料庫的配置,業務程式碼編寫
  2. 增加 user 0-7 號資料庫
  3. 將 user 庫舊錶資料遷移到新增 user_0 庫
  4. 部署服務

但是如果按照上述做法,在第 3、4 步執行期間,如果使用者訪問原 user 庫的資料會有問題。

具體來說:user 庫的舊資料此時已經通過 rename,遷移到了 user_0 庫,但因為部署還沒部署完成,連線資料庫的配置沒有更新。

所以請求依舊會跑去 user 庫查詢,導致查不到資料,後續業務邏輯沒法順序繼續執行。

使用者也會納悶:「這個地方之前進來都有資料的呀,怎麼現在全空了?」

所以,需要確定合理的升級方案,最大程度減少對業務和使用者的影響,

3.1 方案一

這是最簡單的方式。

看監控,挑選沒有流量的時候,進行 db 變更和服務部署。

當然,監控也只是過去的情況,保不準功能上線那天就一直有流量沒停歇過呢。

所以再求穩一點的話,可以發個公告,告知使用者 xx 功能會在 xxx 時間段進行維護,期間不可訪問。

如果有玩農藥(王者榮耀)的朋友應該很熟悉吧,每次版本更新都需要停服,就是這樣的效果哈。

最後在完成之後,進行迴歸測試和新功能測試,看看功能是否正常。

如果正常那就可以去睡覺了,有問題就繼續改 bug 解決;

如果評估沒法在公告所說的截止時間解決,那就只能進行回滾,改日再(jia)戰(ban)。

PS:如果需要對歷史資料進行分庫分表的話,最好進行資料量的對比檢驗。因為我這邊不涉及對歷史資料進行分庫分表,所以這步就省了。

3.2 方案二

這個方案會複雜很多,開發量也會很大。

我這邊就只說關鍵步驟,具體細節就沒法一一寫了。因為要寫的話又多出幾千字的內容,篇幅太長,我估計也沒多少人有耐心看完。

那話說回來,這個方案最大的好處就是業務功能不用停用,所以也就不用熬大夜了。

那要怎麼做呢?

3.2.1 歷史單表資料處理

1、先把 user 庫現有的資料複製一份到 user_0 庫。

2、因為 user 庫的資料是會被修改和新增的。所以當複製完成後,資料依舊存在變化,所以需要新增雙寫邏輯,保證 user_0 庫的資料也能同步到變更。

3、對於資料的讀寫,都支援由開關控制,分別可以控制資料讀寫是請求到哪個資料庫。

4、服務更新完成後,進行兩個庫的資料一致性對比。都沒問題後,開關控制讀寫資料都請求到 user_0 庫

3.2.2 新功能的多表資料處理

因為是新功能,其實不用怎麼特殊處理。

為什麼這麼說呢?

因為我們部署服務的順序肯定是運算元據庫的底層服務先發布,釋出完成後,才對用到底層服務的應用服務進行釋出。

所以作為業務功能入口的應用服務都還沒釋出,此時是不會有新功能資料到達底層服務的。

要是不能保證這個順序,你想下功能入口開放了,使用者請求進來後,底層服務發現找不到這個表,是不是就直接報錯了?

所以才會有上面說的釋出順序,只要保證釋出順序沒錯,那這塊新功能的資料是不需要特殊處理。

3.3 方案優劣對比

其實 2 個方案就是互補的,一個方案的優點就是解決了另一個方案的缺點。

七淅用表格總結一下:

優點缺點
方案一操作簡單,無需編寫複雜程式碼來保證有流量時,業務的正常執行累人,熬大夜太酸爽了;會停用部分業務,影響使用者體驗
方案二業務不必停用,不影響使用者開發成本大

最後,你問我當初是選哪個方案?

那肯定是方案一啊,大不了熬一夜嘛。

不然那麼麻煩的方案,排期又那麼緊張,開發是不可能開發的,這輩子都不可能的。真有什麼問題,大不了就人工介入處理,yyds


文章首發公眾號:七淅在學Java ,持續原創輸出 Java 後端乾貨。

如果對你有幫助的話,可以給個贊再走嗎

相關文章