如何設(shè)計(jì)可以動(dòng)態(tài)擴(kuò)容縮容的分庫分表方案?
本文摘自 Doocs 開源社區(qū)《互聯(lián)網(wǎng) Java 工程師進(jìn)階知識(shí)完全掃盲》手冊(cè),方便讀者隨時(shí)隨地閱讀學(xué)習(xí)。
也歡迎讀者們關(guān)注 GitHub 項(xiàng)目:https://github.com/doocs/advanced-java
面試題
如何設(shè)計(jì)可以動(dòng)態(tài)擴(kuò)容縮容的分庫分表方案?
面試官心理分析
對(duì)于分庫分表來說,主要是面對(duì)以下問題:
?選擇一個(gè)數(shù)據(jù)庫中間件,調(diào)研、學(xué)習(xí)、測試;?設(shè)計(jì)你的分庫分表的一個(gè)方案,你要分成多少個(gè)庫,每個(gè)庫分成多少個(gè)表,比如 3 個(gè)庫,每個(gè)庫 4 個(gè)表;?基于選擇好的數(shù)據(jù)庫中間件,以及在測試環(huán)境建立好的分庫分表的環(huán)境,然后測試一下能否正常進(jìn)行分庫分表的讀寫;?完成單庫單表到分庫分表的遷移,雙寫方案;?線上系統(tǒng)開始基于分庫分表對(duì)外提供服務(wù);?擴(kuò)容了,擴(kuò)容成 6 個(gè)庫,每個(gè)庫需要 12 個(gè)表,你怎么來增加更多庫和表呢?
這個(gè)是你必須面對(duì)的一個(gè)事兒,就是你已經(jīng)弄好分庫分表方案了,然后一堆庫和表都建好了,基于分庫分表中間件的代碼開發(fā)啥的都好了,測試都 ok 了,數(shù)據(jù)能均勻分布到各個(gè)庫和各個(gè)表里去,而且接著你還通過雙寫的方案咔嚓一下上了系統(tǒng),已經(jīng)直接基于分庫分表方案在搞了。
那么現(xiàn)在問題來了,你現(xiàn)在這些庫和表又支撐不住了,要繼續(xù)擴(kuò)容咋辦?這個(gè)可能就是說你的每個(gè)庫的容量又快滿了,或者是你的表數(shù)據(jù)量又太大了,也可能是你每個(gè)庫的寫并發(fā)太高了,你得繼續(xù)擴(kuò)容。
這都是玩兒分庫分表線上必須經(jīng)歷的事兒。
面試題剖析
停機(jī)擴(kuò)容(不推薦)
這個(gè)方案就跟停機(jī)遷移一樣,步驟幾乎一致,唯一的一點(diǎn)就是那個(gè)導(dǎo)數(shù)的工具,是把現(xiàn)有庫表的數(shù)據(jù)抽出來慢慢倒入到新的庫和表里去。但是最好別這么玩兒,有點(diǎn)不太靠譜,因?yàn)榧热?strong style="line-height: 1.75;color: rgb(207, 14, 85);">分庫分表就說明數(shù)據(jù)量實(shí)在是太大了,可能多達(dá)幾億條,甚至幾十億,你這么玩兒,可能會(huì)出問題。
從單庫單表遷移到分庫分表的時(shí)候,數(shù)據(jù)量并不是很大,單表最大也就兩三千萬。那么你寫個(gè)工具,多弄幾臺(tái)機(jī)器并行跑,1 小時(shí)數(shù)據(jù)就導(dǎo)完了。這沒有問題。
如果 3 個(gè)庫 + 12 個(gè)表,跑了一段時(shí)間了,數(shù)據(jù)量都 1~2 億了。光是導(dǎo) 2 億數(shù)據(jù),都要導(dǎo)個(gè)幾個(gè)小時(shí),6 點(diǎn),剛剛導(dǎo)完數(shù)據(jù),還要搞后續(xù)的修改配置,重啟系統(tǒng),測試驗(yàn)證,10 點(diǎn)才可以搞完。所以不能這么搞。
優(yōu)化后的方案
一開始上來就是 32 個(gè)庫,每個(gè)庫 32 個(gè)表,那么總共是 1024 張表。
我可以告訴各位同學(xué),這個(gè)分法,第一,基本上國內(nèi)的互聯(lián)網(wǎng)肯定都是夠用了,第二,無論是并發(fā)支撐還是數(shù)據(jù)量支撐都沒問題。
每個(gè)庫正常承載的寫入并發(fā)量是 1000,那么 32 個(gè)庫就可以承載 32 _ 1000 = 32000 的寫并發(fā),如果每個(gè)庫承載 1500 的寫并發(fā),32 _ 1500 = 48000 的寫并發(fā),接近 5 萬每秒的寫入并發(fā),前面再加一個(gè) MQ,削峰,每秒寫入 MQ 8 萬條數(shù)據(jù),每秒消費(fèi) 5 萬條數(shù)據(jù)。
有些除非是國內(nèi)排名非??壳暗倪@些公司,他們的最核心的系統(tǒng)的數(shù)據(jù)庫,可能會(huì)出現(xiàn)幾百臺(tái)數(shù)據(jù)庫的這么一個(gè)規(guī)模,128 個(gè)庫,256 個(gè)庫,512 個(gè)庫。
1024 張表,假設(shè)每個(gè)表放 500 萬數(shù)據(jù),在 MySQL 里可以放 50 億條數(shù)據(jù)。
每秒 5 萬的寫并發(fā),總共 50 億條數(shù)據(jù),對(duì)于國內(nèi)大部分的互聯(lián)網(wǎng)公司來說,其實(shí)一般來說都?jí)蛄恕?/p>
談分庫分表的擴(kuò)容,第一次分庫分表,就一次性給他分個(gè)夠,32 個(gè)庫,1024 張表,可能對(duì)大部分的中小型互聯(lián)網(wǎng)公司來說,已經(jīng)可以支撐好幾年了。
一個(gè)實(shí)踐是利用?32 * 32?來分庫分表,即分為 32 個(gè)庫,每個(gè)庫里一個(gè)表分為 32 張表。一共就是 1024 張表。根據(jù)某個(gè) id 先根據(jù) 32 取模路由到庫,再根據(jù) 32 取模路由到庫里的表。
| orderId | id % 32 (庫) | id / 32 % 32 (表) |
| 259 | 3 | 8 |
| 1189 | 5 | 5 |
| 352 | 0 | 11 |
| 4593 | 17 | 15 |
剛開始的時(shí)候,這個(gè)庫可能就是邏輯庫,建在一個(gè)數(shù)據(jù)庫上的,就是一個(gè) MySQL 服務(wù)器可能建了 n 個(gè)庫,比如 32 個(gè)庫。后面如果要拆分,就是不斷在庫和 MySQL 服務(wù)器之間做遷移就可以了。然后系統(tǒng)配合改一下配置即可。
比如說最多可以擴(kuò)展到 32 個(gè)數(shù)據(jù)庫服務(wù)器,每個(gè)數(shù)據(jù)庫服務(wù)器是一個(gè)庫。如果還是不夠?最多可以擴(kuò)展到 1024 個(gè)數(shù)據(jù)庫服務(wù)器,每個(gè)數(shù)據(jù)庫服務(wù)器上面一個(gè)庫一個(gè)表。因?yàn)樽疃嗍?1024 個(gè)表。
這么搞,是不用自己寫代碼做數(shù)據(jù)遷移的,都交給 DBA 來搞好了,但是 DBA 確實(shí)是需要做一些庫表遷移的工作,但是總比你自己寫代碼,然后抽數(shù)據(jù)導(dǎo)數(shù)據(jù)來的效率高得多吧。
哪怕是要減少庫的數(shù)量,也很簡單,其實(shí)說白了就是按倍數(shù)縮容就可以了,然后修改一下路由規(guī)則。
這里對(duì)步驟做一個(gè)總結(jié):
1.設(shè)定好幾臺(tái)數(shù)據(jù)庫服務(wù)器,每臺(tái)服務(wù)器上幾個(gè)庫,每個(gè)庫多少個(gè)表,推薦是 32 庫 * 32 表,對(duì)于大部分公司來說,可能幾年都?jí)蛄恕?/span>2.路由的規(guī)則,orderId 模 32 = 庫,orderId / 32 模 32 = 表3.擴(kuò)容的時(shí)候,申請(qǐng)?jiān)黾痈嗟臄?shù)據(jù)庫服務(wù)器,裝好 MySQL,呈倍數(shù)擴(kuò)容,4 臺(tái)服務(wù)器,擴(kuò)到 8 臺(tái)服務(wù)器,再到 16 臺(tái)服務(wù)器。4.由 DBA 負(fù)責(zé)將原先數(shù)據(jù)庫服務(wù)器的庫,遷移到新的數(shù)據(jù)庫服務(wù)器上去,庫遷移是有一些便捷的工具的。5.我們這邊就是修改一下配置,調(diào)整遷移的庫所在數(shù)據(jù)庫服務(wù)器的地址。6.重新發(fā)布系統(tǒng),上線,原先的路由規(guī)則變都不用變,直接可以基于 n 倍的數(shù)據(jù)庫服務(wù)器的資源,繼續(xù)進(jìn)行線上系統(tǒng)的提供服務(wù)。
推薦閱讀
?分庫分表之后,id 主鍵如何處理??MySQL 主從同步延時(shí)問題與解決方案?MQ 消息重復(fù)消費(fèi)問題與解決方案?MQ 消息丟失問題與解決方案?MQ 消息錯(cuò)亂問題與解決方案
歡迎關(guān)注公眾號(hào)「Doocs」,第一時(shí)間跟你們分享好玩、實(shí)用的技術(shù)文章與業(yè)內(nèi)最新資訊。
