Mysql分庫(kù)分表實(shí)現(xiàn)方式
1、背景
Mysql作為做流行的開源數(shù)據(jù)庫(kù),在各大互聯(lián)網(wǎng)公司被廣泛使用。通常我們用一個(gè)庫(kù)就可以滿足需求,但是隨著業(yè)務(wù)的增長(zhǎng),數(shù)據(jù)量和并發(fā)量迅速膨脹。
Mysql單表數(shù)據(jù)量到百萬以上的時(shí)候,查詢效率就會(huì)受到影響,另外Mysql單庫(kù)能承受的并發(fā)也有限。
這個(gè)時(shí)候我們需要做分庫(kù)分表,來提高數(shù)據(jù)庫(kù)的性能和擴(kuò)展性
2、為什么分庫(kù)分表
2.1 分表
單表數(shù)據(jù)量太大會(huì)極大的影響sql執(zhí)行效率,一般來說單表達(dá)到幾百萬的時(shí)候,性能就相對(duì)較差了,就需要分表了
2.2 分庫(kù)
單個(gè)庫(kù)一般最大支持到2000并發(fā),超過就需要分庫(kù)了,一個(gè)健康的單庫(kù)并發(fā)值最好控制在1000左右
| # | 分庫(kù)分表前 | 分庫(kù)分表后 |
|---|---|---|
| 并發(fā)支撐情況 | MySQL 單機(jī)部署,扛不住高并發(fā) | MySQL 從單機(jī)到多機(jī),能承受的并發(fā)增加了多倍 |
| 磁盤使用情況 | MySQL 單機(jī)磁盤容量幾乎撐滿 | 拆分為多個(gè)庫(kù),數(shù)據(jù)庫(kù)服務(wù)器磁盤使用率大大降低 |
| SQL 執(zhí)行性能 | 單表數(shù)據(jù)量太大,SQL 越跑越慢 | 單表數(shù)據(jù)量減少,SQL 執(zhí)行效率明顯提升 |
數(shù)據(jù)量大,就分表;并發(fā)高,就分庫(kù)。
2.3 帶來的問題
- join 操作 : 同一個(gè)數(shù)據(jù)庫(kù)中的表分布在了不同的數(shù)據(jù)庫(kù)中,導(dǎo)致無法使用 join 操作。這樣就導(dǎo)致我們需要手動(dòng)進(jìn)行數(shù)據(jù)的封裝,比如你在一個(gè)數(shù)據(jù)庫(kù)中查詢到一個(gè)數(shù)據(jù)之后,再根據(jù)這個(gè)數(shù)據(jù)去另外一個(gè)數(shù)據(jù)庫(kù)中找對(duì)應(yīng)的數(shù)據(jù)。
- 事務(wù)問題 :同一個(gè)數(shù)據(jù)庫(kù)中的表分布在了不同的數(shù)據(jù)庫(kù)中,如果單個(gè)操作涉及到多個(gè)數(shù)據(jù)庫(kù),那么數(shù)據(jù)庫(kù)自帶的事務(wù)就無法滿足我們的要求了。
- 分布式 id :分庫(kù)之后, 數(shù)據(jù)遍布在不同服務(wù)器上的數(shù)據(jù)庫(kù),數(shù)據(jù)庫(kù)的自增主鍵已經(jīng)沒辦法滿足生成的主鍵唯一了。我們?nèi)绾螢椴煌臄?shù)據(jù)節(jié)點(diǎn)生成全局唯一主鍵呢?這個(gè)時(shí)候,我們就需要為我們的系統(tǒng)引入分布式 id 了。
3、不同分庫(kù)中間件的優(yōu)缺點(diǎn)
3.1 Sharding-jdbc
1、當(dāng)當(dāng)開源的,client層方案
2、支持分庫(kù)分表,讀寫分離,分布式ID生成,柔性事物(最大努力送達(dá)型事物,TCC事物)
3、社區(qū)比較活躍
- 優(yōu)點(diǎn): 優(yōu)點(diǎn)在于不用部署,運(yùn)維成本低,不需要代理層的二次轉(zhuǎn)發(fā)請(qǐng)求,性能很高
- 缺點(diǎn): 各個(gè)系統(tǒng)都需要耦合Sharding-jdbc依賴,如果需要升級(jí),各個(gè)系統(tǒng)需要重新部署
適合中小型公司
3.2 Mycat
1、proxy層方案
2、支持的功能完善
3、很流行,社區(qū)非常活躍
- 優(yōu)點(diǎn): 對(duì)各個(gè)項(xiàng)目透明,升級(jí)只需要升級(jí)中間件
- 缺點(diǎn): 需要部署,自己運(yùn)維一套中間件,運(yùn)維成本高
適合大型公司
3.3 如何選擇
推薦使用sharding-jdbc和mycat:
- 小型公司選用sharding-jdbc,client層方案輕便,而且維護(hù)成本低,不需要額外增派人手,而且中小型公司系統(tǒng)復(fù)雜度會(huì)低一些,項(xiàng)目也沒那么多
- 中大型公司最好還是選用mycat這類proxy層方案,因?yàn)榭赡艽蠊鞠到y(tǒng)和項(xiàng)目非常多,團(tuán)隊(duì)很大,人員充足,那么最好是專門弄個(gè)人來研究和維護(hù)mycat,然后大量項(xiàng)目直接透明使用即可
4、如何拆分
4.1、水平拆分(用的最多)
把一個(gè)表的數(shù)據(jù)拆分到多個(gè)表中,但是每個(gè)表的結(jié)構(gòu)都一樣,把數(shù)據(jù)均勻的放到不同的表里,用多個(gè)表來抗并發(fā)
根據(jù)特定字段來拆分:
比如訂單表根據(jù)訂單ID來拆分,分到32個(gè)庫(kù),每個(gè)庫(kù)32張表,那么orderID%32決定落在哪個(gè)庫(kù),orderID/32%32決定落在那個(gè)表。
而且這兒還有兩種分庫(kù)分表的方式:
- 一種是按照 range 來分,就是每個(gè)庫(kù)一段連續(xù)的數(shù)據(jù),這個(gè)一般是按比如時(shí)間范圍來的,但是這種一般較少用,因?yàn)楹苋菀桩a(chǎn)生熱點(diǎn)問題,大量的流量都打在最新的數(shù)據(jù)上了。
- 是按照某個(gè)字段 hash 一下均勻分散,這個(gè)較為常用。
各自優(yōu)缺點(diǎn):
- range 來分,好處在于說,擴(kuò)容的時(shí)候很簡(jiǎn)單,因?yàn)槟阒灰A(yù)備好,給每個(gè)月都準(zhǔn)備一個(gè)庫(kù)就可以了,到了一個(gè)新的月份的時(shí)候,自然而然,就會(huì)寫新的庫(kù)了;缺點(diǎn),但是大部分的請(qǐng)求,都是訪問最新的數(shù)據(jù)。實(shí)際生產(chǎn)用 range,要看場(chǎng)景。
- hash 分發(fā),好處在于說,可以平均分配每個(gè)庫(kù)的數(shù)據(jù)量和請(qǐng)求壓力;壞處在于說擴(kuò)容起來比較麻煩,會(huì)有一個(gè)數(shù)據(jù)遷移的過程,之前的數(shù)據(jù)需要重新計(jì)算 hash 值重新分配到不同的庫(kù)或表。
4.2、垂直拆分
把一個(gè)有很多字段的表拆分到多個(gè)表或者多個(gè)庫(kù)上去,把訪問頻率高的熱點(diǎn)字段和訪問頻率低的非熱點(diǎn)字段分開放到不同的表。因?yàn)閿?shù)據(jù)庫(kù)有緩存,熱點(diǎn)字段越少,緩存里可以存更多的行
5、分庫(kù)分表方案確定后,還需要解決以下問題
- 如何實(shí)現(xiàn)數(shù)據(jù)從單庫(kù)單表到分庫(kù)分表的遷移?
- 表和庫(kù)的數(shù)量又遇到瓶頸怎么辦,如何處理?
- 分庫(kù)分表后的ID如何處理?
- 分庫(kù)分表后的事務(wù)如何處理?
5.1 如何設(shè)計(jì)才可以讓系統(tǒng)從未分庫(kù)分表平滑的動(dòng)態(tài)切換到分庫(kù)分表上?
停機(jī)遷移
- 一種方式是直接停機(jī)遷移,需要再深夜沒有流量的時(shí)候進(jìn)行
- 把數(shù)據(jù)遷移到分庫(kù)分表數(shù)據(jù)庫(kù)上,然后重啟系統(tǒng)就可以,這種方式需要停機(jī),并不是平滑遷移
雙寫遷移方案
這種方案不用停機(jī)。
簡(jiǎn)單來說,就是在線上系統(tǒng)里面,之前所有寫庫(kù)的地方,增刪改操作,除了對(duì)老庫(kù)增刪改,都加上對(duì)新庫(kù)的增刪改,這就是所謂的雙寫,同時(shí)寫倆庫(kù),老庫(kù)和新庫(kù)。
然后系統(tǒng)部署之后,新庫(kù)數(shù)據(jù)差太遠(yuǎn),用之前說的導(dǎo)數(shù)工具,跑起來讀老庫(kù)數(shù)據(jù)寫新庫(kù),寫的時(shí)候要根據(jù) gmt_modified 這類字段判斷這條數(shù)據(jù)最后修改的時(shí)間,除非是讀出來的數(shù)據(jù)在新庫(kù)里沒有,或者是比新庫(kù)的數(shù)據(jù)新才會(huì)寫。簡(jiǎn)單來說,就是不允許用老數(shù)據(jù)覆蓋新數(shù)據(jù)。
導(dǎo)完一輪之后,有可能數(shù)據(jù)還是存在不一致,那么就程序自動(dòng)做一輪校驗(yàn),比對(duì)新老庫(kù)每個(gè)表的每條數(shù)據(jù),接著如果有不一樣的,就針對(duì)那些不一樣的,從老庫(kù)讀數(shù)據(jù)再次寫。反復(fù)循環(huán),直到兩個(gè)庫(kù)每個(gè)表的數(shù)據(jù)都完全一致為止。
接著當(dāng)數(shù)據(jù)完全一致了,就 ok 了,基于僅僅使用分庫(kù)分表的最新代碼,重新部署一次,不就僅僅基于分庫(kù)分表在操作了么,還沒有幾個(gè)小時(shí)的停機(jī)時(shí)間,很穩(wěn)。所以現(xiàn)在基本玩兒數(shù)據(jù)遷移之類的,都是這么干的。
這個(gè)方法的缺點(diǎn)就是代碼里需要同時(shí)支持兩個(gè)庫(kù)的過度階段,稍微麻煩一點(diǎn),但是不需要停機(jī),可以平滑遷移。
5.2 動(dòng)態(tài)擴(kuò)容縮容的分庫(kù)分表方案?
分庫(kù)分表完成后,如果數(shù)據(jù)繼續(xù)增加,原來的方案不能滿足業(yè)務(wù)了,需要繼續(xù)擴(kuò)大更多庫(kù)更多表,那么還要再來一次分庫(kù)分表嗎?
這樣處理會(huì)很麻煩,因?yàn)楹罄m(xù)可能還不夠,分庫(kù)分表數(shù)據(jù)遷移非常費(fèi)勁。那么我們可以采取一次性分足夠多的庫(kù)和表,這樣避免了后續(xù)遷移數(shù)據(jù)的問題。
一開始分庫(kù)一次性分夠,32庫(kù)*32表,一共1024張表,根據(jù)某個(gè) id 先根據(jù) 32 取模路由到庫(kù),再根據(jù) 32 取模路由到庫(kù)里的表。
| orderId | id % 32 (庫(kù)) | id / 32 % 32 (表) |
|---|---|---|
| 259 | 3 | 8 |
| 1189 | 5 | 5 |
| 352 | 0 | 11 |
| 4593 | 17 | 15 |
剛開始的時(shí)候,這個(gè)庫(kù)可能就是邏輯庫(kù),建在一個(gè)數(shù)據(jù)庫(kù)上的,就是一個(gè) MySQL 服務(wù)器可能建了 n 個(gè)庫(kù),比如 32 個(gè)庫(kù)。后面如果要拆分,就是不斷在庫(kù)和 MySQL 服務(wù)器之間做遷移就可以了。然后系統(tǒng)配合改一下配置即可。
比如說最多可以擴(kuò)展到 32 個(gè)數(shù)據(jù)庫(kù)服務(wù)器,每個(gè)數(shù)據(jù)庫(kù)服務(wù)器是一個(gè)庫(kù)。如果還是不夠?最多可以擴(kuò)展到 1024 個(gè)數(shù)據(jù)庫(kù)服務(wù)器,每個(gè)數(shù)據(jù)庫(kù)服務(wù)器上面一個(gè)庫(kù)一個(gè)表。因?yàn)樽疃嗍?1024 個(gè)表。
這么搞,是不用自己寫代碼做數(shù)據(jù)遷移的,都交給 DBA 來搞好了,但是 DBA 確實(shí)是需要做一些庫(kù)表遷移的工作,但是總比你自己寫代碼,然后抽數(shù)據(jù)導(dǎo)數(shù)據(jù)來的效率高得多吧。
這里對(duì)步驟做一個(gè)總結(jié):
- 設(shè)定好幾臺(tái)數(shù)據(jù)庫(kù)服務(wù)器,每臺(tái)服務(wù)器上幾個(gè)庫(kù),每個(gè)庫(kù)多少個(gè)表,推薦是 32 庫(kù) * 32 表,對(duì)于大部分公司來說,可能幾年都?jí)蛄恕?/li>
- 路由的規(guī)則,orderId 模 32 = 庫(kù),orderId / 32 模 32 = 表
- 擴(kuò)容的時(shí)候,申請(qǐng)?jiān)黾痈嗟臄?shù)據(jù)庫(kù)服務(wù)器,裝好 MySQL,呈倍數(shù)擴(kuò)容,4 臺(tái)服務(wù)器,擴(kuò)到 8 臺(tái)服務(wù)器,再到 16 臺(tái)服務(wù)器。
- 由 DBA 負(fù)責(zé)將原先數(shù)據(jù)庫(kù)服務(wù)器的庫(kù),遷移到新的數(shù)據(jù)庫(kù)服務(wù)器上去,庫(kù)遷移是有一些便捷的工具的。
- 我們這邊就是修改一下配置,調(diào)整遷移的庫(kù)所在數(shù)據(jù)庫(kù)服務(wù)器的地址。
- 重新發(fā)布系統(tǒng),上線,原先的路由規(guī)則變都不用變,直接可以基于 n 倍的數(shù)據(jù)庫(kù)服務(wù)器的資源,繼續(xù)進(jìn)行線上系統(tǒng)的提供服務(wù)。
6、分庫(kù)分表之后,id 主鍵如何處理?
6.1、設(shè)置數(shù)據(jù)庫(kù) sequence 或者表自增字段步長(zhǎng)
適合的場(chǎng)景:
- 在用戶防止產(chǎn)生的 ID 重復(fù)時(shí),這種方案實(shí)現(xiàn)起來比較簡(jiǎn)單,也能達(dá)到性能目標(biāo)。
- 但是服務(wù)節(jié)點(diǎn)固定,步長(zhǎng)也固定,將來如果還要增加服務(wù)節(jié)點(diǎn),就不好搞了。
6.2、UUID
不具有有序性,作為ID對(duì)索引不友好,會(huì)導(dǎo)致 B+ 樹索引在寫的時(shí)候有過多的隨機(jī)寫操作(連續(xù)的 ID 可以產(chǎn)生部分順序?qū)懀?,不適合做主鍵。
適合的場(chǎng)景:
- 如果你是要隨機(jī)生成個(gè)什么文件名、編號(hào)之類的
- 你可以用 UUID,但是作為主鍵是不能用 UUID 的
6.3、獲取當(dāng)前系統(tǒng)時(shí)間, 并發(fā)高的時(shí)候可能會(huì)重復(fù)
適合的場(chǎng)景:
- 一般如果用這個(gè)方案,是將當(dāng)前時(shí)間跟很多其他的業(yè)務(wù)字段拼接起來,作為一個(gè) id,如果業(yè)務(wù)上你覺得可以接受,那么也是可以的。
- 你可以將別的業(yè)務(wù)字段值跟當(dāng)前時(shí)間拼接起來,組成一個(gè)全局唯一的編號(hào)
6.4、snowflake 算法
- snowflake 算法是 twitter 開源的分布式 id 生成算法,采用 Scala 語言實(shí)現(xiàn),
- 是把一個(gè) 64 位的 long 型的 id,1 個(gè) bit 是不用的,用其中的 41 bits 作為毫秒數(shù),用 10 bits 作為工作機(jī)器 id,12 bits 作為序列號(hào)。
7、分庫(kù)分表后,事務(wù)如何處理?
分庫(kù)分表以后,可能會(huì)出現(xiàn)跨庫(kù)事務(wù),這種事務(wù)怎么處理呢?
7.1 優(yōu)化方案避免出現(xiàn)跨庫(kù)事務(wù)
- 首先我們做分庫(kù)分表的時(shí)候,盡量避免出現(xiàn)跨庫(kù)的事務(wù)的場(chǎng)景,
- 比如訂單表,分庫(kù)的時(shí)候根據(jù)訂單ID開拆分,那么跟訂單相關(guān)的其他表也按訂單ID來拆分,這樣針對(duì)一個(gè)訂單的事務(wù)就可以控制在一個(gè)庫(kù)中,避免了跨庫(kù)事務(wù)。
7.2 跨庫(kù)事務(wù)的處理
- 如果無法避免,分庫(kù)分表一般是單系統(tǒng)對(duì)應(yīng)多個(gè)庫(kù),那么數(shù)據(jù)庫(kù)的XA事務(wù)正好可以處理這種場(chǎng)景,
- sharding-jdbc可以支持?jǐn)?shù)據(jù)庫(kù)的XA事務(wù),通過@DSTransactional注解就可以實(shí)現(xiàn)跨庫(kù)事務(wù)。
Mycat也能支持XA事務(wù)。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
5個(gè)MySQL GUI工具推薦,幫助你進(jìn)行數(shù)據(jù)庫(kù)管理
這篇文章主要介紹了5個(gè)MySQL GUI工具推薦,幫助大家更好的進(jìn)行MySQL數(shù)據(jù)庫(kù)管理,感興趣的朋友可以了解下2020-08-08
探討:sql插入空,默認(rèn)1900-01-01 00:00:00.000的解決方法詳解
本篇文章是對(duì)sql插入空,默認(rèn)1900-01-01 00:00:00.000的解決方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06
CentOS6.7 mysql5.6.33修改數(shù)據(jù)文件位置的方法
mysql存放的數(shù)據(jù)文件,分區(qū)容量較小,目前已經(jīng)滿,導(dǎo)致mysql連接不上,怎么解決呢?下面小編給大家分享CentOS6.7 mysql5.6.33修改數(shù)據(jù)文件位置的方法,一起看看吧2017-06-06
mysql隨機(jī)查詢?nèi)舾蓷l數(shù)據(jù)的方法
這篇文章主要介紹了mysql中獲取隨機(jī)內(nèi)容的方法,需要的朋友可以參考下2013-10-10
詳解MySQL like如何查詢包含''%''的字段(ESCAPE用法)
這篇文章主要介紹了詳解MySQL like如何查詢包含'%'的字段(ESCAPE用法),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12
MySQL數(shù)據(jù)庫(kù)列的增刪改實(shí)現(xiàn)方法
這篇文章主要介紹了MySQL數(shù)據(jù)庫(kù)列的增刪改實(shí)現(xiàn)方法,結(jié)合實(shí)例形式分析了mysql數(shù)據(jù)庫(kù)針對(duì)列的增加、修改、刪除等相關(guān)操作sql命令及使用技巧,需要的朋友可以參考下2019-03-03
Shell下實(shí)現(xiàn)免密碼快速登陸MySQL數(shù)據(jù)庫(kù)的方法
這篇文章主要給大家介紹了在Shell下實(shí)現(xiàn)免密碼快速登陸MySQL數(shù)據(jù)庫(kù)的方法,文中通過示例代碼一步步介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面跟著小編來一起看看吧。2017-06-06
MySQL UNION操作符基礎(chǔ)知識(shí)點(diǎn)
在本文里小編給大家整理了關(guān)于MySQL UNION操作符的相關(guān)知識(shí)點(diǎn)內(nèi)容,需要的朋友們跟著學(xué)習(xí)下。2019-02-02

