mysql分表之後怎麼平滑上線?

程式設計師小飯發表於2021-10-28

分表的目的

專案開發中,我們的資料庫資料越來越大,隨之而來的是單個表中資料太多。以至於查詢資料變慢,而且由於表的鎖機制導致應用操作也受到嚴重影響,出現了資料庫效能瓶頸。

當出現這種情況時,我們可以考慮分表,即將單個資料庫表進行拆分,拆分成多個資料表,然後使用者訪問的時候,根據一定的演算法,讓使用者訪問不同的表,這樣資料分散到多個資料表中,減少了單個資料表的訪問壓力。提升了資料庫訪問效能。

舉個例子

舉個例子
舉個例子

比如我們們最常見的使用者表(user表)

id user_id 其他欄位
主鍵id 使用者id 其他欄位

我們們一般都會用user_id去查詢對應的使用者資訊,但是隨著業務的增長,這張表會越來越大,甚至上億,嚴重影響了查詢效能。 所以我們們就會對這張表進行分表處理,分到多張表減小查詢壓力

分表策略

以分10張表為例(具體分多少張表 根據實際情況來估算) 首先我們們建10張表 user1、user2、user3。。。。。user10

一般情況下,我們都會用作為索引的欄位(user_id)進行取模處理。想分多少張表,就按照多少取模,比如這個case就是10

$table_name = $user_id % 10;

按照上面的取模公式

  • user_id為1295的會落在user5裡面
  • user_id為8634的會落在user4裡面
  • 。。。。。。。

每次CURD根據上面查詢表的策略進行就行了,這個問題不大,我們暫且先不多說。

已經上線的執行中的表怎麼辦?

其實上面的方法大家應該都知道怎麼用,但是有個問題,已經上線了的表怎麼辦?那張表的資料線上上是一直被查詢或者改變的。如何能夠進行平滑的分表,並且讓使用者無感知呢?

方法1

直接上線,提前寫個指令碼,指令碼內容是把舊錶(user)的資料同步到user1表到user10表,一上線了趕緊執行

這種方法明顯是行不通的,主要是存在以下問題

  • 如果執行過程中指令碼有問題怎麼辦?程式碼全部回滾?
  • 指令碼把把舊錶(user)的資料同步到user1表到user10表,這個指令碼得執行多久? 如果是1個小時,那麼這段時間線上和這張表相關的業務都是不正常的

這顯然是行不通的,對線上影響很大。

方法2

先寫個同步資料的指令碼,指令碼內容是把舊錶(user)的資料同步到user1表到user10表,指令碼同步完了再上線。

這個方法看起來友好了一些,不過也存在一些問題。

  • 指令碼同步完,立即上線,這兩件事之間是有一些時間差的,這個時間差中線上表可能有一些改動,這些改動怎麼辦?

以上兩種方法看起來貌似都行不通,所以看來得來點不一樣的了。我們們直接看結論。

步驟1 上線雙寫

首先我們們把雙寫上線了,什麼意思呢?比如user_id=123,對於增加,刪除,修改操作來說,我們們既操作user表,也操作user_id=123對應的user3表。

function modify($user_id){  //包含增加,刪除,修改操作
  modify_user();  //modify user表
  $table_name = $user_id % 10;
  modify_user($table_name) //modify對應的分表
}

因為查詢的部分還是在user表中查詢的,所以上面的操作對線上使用者是無任何影響的。

步驟2 全量同步

寫一個全量同步user表到user1-user10的表,最好找個低峰期執行指令碼,以防萬一影響user表的查詢

這一步執行之後,因為我們們之前上線了雙寫(見步驟1),所以user表和user1-user10表之間的資料已經是完全一致的了。

步驟3 查詢新表資料

將查詢的部分改到user1-user10

因為前面兩個步驟我們們已經保證了user表和各個分表之間的資料完全一致性,所以直接把查詢的部分改掉是沒有任何問題的

如果按照以上步驟執行,那麼對線上的資料是沒有任何影響的,而且我們線上就是這麼操作了,經過了多次實踐確保不會出問題,放心使用即可。如果這篇文章幫助到了你,記得點個贊哦。

相關文章