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