導航:
【Java筆記+踩坑匯總】Java基礎+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外賣/黑馬旅游/谷粒商城/學成在線+設計模式+面試題匯總+性能調(diào)優(yōu)/架構設計+源碼-CSDN博客
目錄
一、分庫分表基本概念
二、分庫分表的場景和核心思想
三、分庫分表具體步驟
3.1 分庫分表的原則:能不分就不分
3.2 目標評估
3.3 表拆分
3.3.1 業(yè)務層面拆分
3.3.1.1 混合業(yè)務拆分
3.3.1.2 冷熱分離
3.3.2 數(shù)據(jù)層面拆分
3.4 分表字段(sharding_key)選擇
3.5 代碼改造
3.6 數(shù)據(jù)遷移
3.6.1 增量同步
3.6.2 全量同步
3.7 數(shù)據(jù)一致性校驗和補償
3.8 灰度切讀
3.9 停舊庫、寫新庫
一、分庫分表基本概念
只分表:
單表數(shù)據(jù)量大,讀寫出現(xiàn)瓶頸,這個表所在的庫還可以支撐未來幾年的增長。
只分庫:
整個數(shù)據(jù)庫讀寫出現(xiàn)性能瓶頸,例如數(shù)據(jù)庫連接數(shù)被打滿了(MySQL最大連接數(shù)默認150),或者并發(fā)量太大導致單個數(shù)據(jù)庫已經(jīng)無法滿足日常的讀寫需求,就需要將整個庫拆開。
分庫分表:
單表數(shù)據(jù)量大,所在庫也出現(xiàn)性能瓶頸,就要既分庫又分表。
垂直拆分:
把字段分開。例如spu表的pic字段特別長,建議把這個pic字段拆到另一個表(同庫b不同庫。
水平拆分:
把記錄分開。例如表數(shù)據(jù)量到達百萬,我們拆成四張20萬的表。
二、分庫分表的場景和核心思想
一般情況下,單表數(shù)據(jù)量到達千萬級別,就可以考慮分庫分表了。
具體是否需要分庫分表還是要看具體的業(yè)務場景,例如流水表、記錄表,數(shù)據(jù)量非常容易到達千萬級、億萬級,需要在設計數(shù)據(jù)庫表的階段就進行分表,還有一些表雖然數(shù)據(jù)量只有幾百萬,但字段非常多,而且有很多text、blog格式的字段,查詢性能也會很慢,可以考慮分庫分表。
數(shù)據(jù)量增長情況 | 數(shù)據(jù)表類型 | 優(yōu)化核心思想 |
---|---|---|
數(shù)據(jù)量為千萬級,是一個相對穩(wěn)定的數(shù)據(jù)量 | 狀態(tài)表 | 能不拆就不拆讀需求水平擴展 |
數(shù)據(jù)量為千萬級,可能達到億級或者更高 | 流水表 | 業(yè)務拆分,面向分布式存儲設計 |
數(shù)據(jù)量為千萬級,可能達到億級或者更高 | 流水表 | 設計數(shù)據(jù)統(tǒng)計需求存儲的分布式擴展 |
數(shù)據(jù)量為千萬級,不應該有這么多的數(shù)據(jù) | 配置表 | 小而簡,避免大一統(tǒng) |
三、分庫分表具體步驟
3.1 分庫分表的原則:能不分就不分
1.優(yōu)先MySQL調(diào)優(yōu),能不分就不分。
數(shù)據(jù)量能穩(wěn)定在千萬級,近幾年不會到達億級,其實是不用著急拆的,先嘗試MySQL調(diào)優(yōu),優(yōu)化讀寫性能。只有在MySQL調(diào)優(yōu)已經(jīng)無法解決慢查詢問題時,才可以考慮分庫分表。
MySQL調(diào)優(yōu):
【MySQL調(diào)優(yōu)】如何進行MySQL調(diào)優(yōu)?一篇文章就夠了!-CSDN博客
2.分片數(shù)量盡量少。
分片盡量均勻分布在多個 DataHost 上,因為一個查詢 SQL 跨分片越多,則總體性能越差,雖然要好于所有數(shù)據(jù)在一個分片的結果,只在必要的時候進行擴容,增加分片數(shù)量。
3.不要一個事務里跨越多個分片查詢
盡量不要在一個事務中的 SQL 跨越多個分片,分布式事務一直是個不好處理的問題。
3.2 目標評估
評估需要拆分成幾個庫、幾個表。
舉例:
當前20億,5年后評估為100億。分幾個表? 分幾個庫?解答:一個合理的答案,1024個表,16個庫按1024個表算,拆分完單表200萬,5年后為1000萬.1024個表*200w≈100億
3.3 表拆分
3.3.1 業(yè)務層面拆分
3.3.1.1 混合業(yè)務拆分
混合業(yè)務拆分:將混合業(yè)務拆分為獨立業(yè)務。
業(yè)務場景舉例:
-
電商網(wǎng)站:一個典型的混合業(yè)務,包含用戶信息、訂單信息、商品信息等。可以將用戶信息、訂單信息和商品信息分別拆分到不同的庫或表中,以減少數(shù)據(jù)冗余并提高訪問效率。
-
社交媒體平臺:包含用戶信息、好友關系、動態(tài)信息等。可以將用戶信息和好友關系分離存儲,以便更好地支持好友關系的查詢和更新。
-
在線游戲:涉及角色信息、道具信息、戰(zhàn)斗日志等??梢詫⒔巧畔⒑偷谰咝畔⒉鸱值讲煌谋碇?,以提升查詢效率,并將戰(zhàn)斗日志存儲到日志數(shù)據(jù)庫中,以減輕主數(shù)據(jù)庫的負載。
-
物流系統(tǒng):包含訂單信息、配送信息、運輸信息等??梢詫⒂唵涡畔?、配送信息和運輸信息分別拆分到不同的表中,以便更好地支持訂單的查詢和跟蹤。
3.3.1.2 冷熱分離
冷熱分離:將常用的“熱”數(shù)據(jù)和不常使用的“冷”數(shù)據(jù)分開存儲。即在處理數(shù)據(jù)時將數(shù)據(jù)庫分成冷庫和熱庫,冷庫存放那些走到終態(tài)、不常使用的數(shù)據(jù),熱庫存放還需要修改、經(jīng)常使用的數(shù)據(jù)。
什么情況下可以使用冷熱分離?
- 數(shù)據(jù)走到終態(tài)后只有讀沒有寫的需求。例如訂單完結后基本只會讀不會改。
- 用戶能接受新舊數(shù)據(jù)分開查詢。比如有些電商網(wǎng)站默認只讓查詢3個月內(nèi)的訂單,如果要查詢3個月前的訂單,還需要訪問其他的頁面。
業(yè)務場景舉例:
-
郵件系統(tǒng):郵件系統(tǒng)中最近郵件是用戶經(jīng)常訪問和修改的,三個月前的郵件或已歸檔的郵件不經(jīng)常訪問的??梢詫⒂脩舻氖占?、發(fā)件箱里最近三個月的郵件放在一個庫里(熱庫),之前的郵件或者已讀的郵件放在另一個庫里(冷酷)。
-
日志系統(tǒng):在大型應用中,日志數(shù)據(jù)是非常龐大的,但并不是所有日志都需要經(jīng)常查詢或分析??梢詫⒆罱欢螘r間的活動日志存放在熱庫中,而將過去的歷史日志存放在冷庫中,以減輕熱庫的負載和優(yōu)化查詢性能。
-
社交媒體平臺:社交媒體平臺上的用戶數(shù)據(jù)量通常很大,但是只有少部分用戶是活躍的,并且只有少量用戶的數(shù)據(jù)會頻繁訪問和更新,如果所有用戶都放在同一個庫里,勢必會影響活躍用戶的查詢效率??梢詫⒒钴S用戶的個人信息、好友關系等存放在熱庫中,而將不活躍用戶的數(shù)據(jù)存放在冷庫中,以提升熱庫的性能和減少冷庫的存儲成本。
-
電商平臺:電商平臺上的商品數(shù)據(jù)也可以進行冷熱分離。熱庫中存放熱門商品的基本信息和庫存等,以支持頻繁的查詢和更新操作,而將不活躍或下架的商品信息存放在冷庫中,以減少熱庫的負載和優(yōu)化查詢性能。
-
客服工單:在我們?nèi)粘2僮鲿r,經(jīng)常能看到查詢歷史工單時會有個“近三個月工單”的選項,實際業(yè)務場景中,用戶基本只會關注近三個月工單,而且這些工單也會經(jīng)常需要進行修改、刪除的操作,而對很早期的歷史訂單基本就沒有修改、刪除的需求,只有少量的查詢需求。
3.3.2 數(shù)據(jù)層面拆分
-
按日期拆分:這種使用方式比較普遍,尤其是按照日期維度的拆分,其實在程序?qū)用娴母膭雍苄?,但是擴展性方面的收益很大。
- 日維度拆分,如test_20191021
- 月維度拆分,如test_201910
- 年維度拆分,如test_2019
-
按主鍵范圍拆分:例如【1,200w】主鍵在一個表,【200w,400w】主鍵在一個表。優(yōu)點是單表數(shù)據(jù)量可控。缺點是流量無法分攤,寫操作集中在最后面的表。
-
中間表映射:表隨意拆分,引入中間表記錄查詢的字段值,以及它對應的數(shù)據(jù)在哪個表里。優(yōu)點是靈活。確定是引入中間表讓流程變復雜。
-
hash切分:sharding_key%N。優(yōu)點是數(shù)據(jù)分片均勻,流量分攤。缺點是擴容需要遷移數(shù)據(jù),跨節(jié)點查詢問題。
-
按分區(qū)拆分:hash,range等方式。不建議,因為數(shù)據(jù)其實難以實現(xiàn)水平擴展。
3.4 分表字段(sharding_key)選擇
選擇最佳的分表字段是一個需要仔細考慮的問題。最佳的分表字段應該是能夠讓數(shù)據(jù)分布均勻、頻繁查詢的字段以及不可變的字段。通過選擇最佳的分表字段,可以提高系統(tǒng)的性能和查詢效率。
常用字段:
-
主鍵ID:頻繁查詢并且唯一,非常適合作分表字段。例如,在用戶表中,用戶ID作為分表字段是一個不錯的選擇,因為用戶ID是唯一的,而且在查詢用戶信息時經(jīng)常會用到。
-
時間字段:如果業(yè)務需要按時間范圍查詢數(shù)據(jù),那么選擇時間字段作為分表字段是合理的。例如,在日志表中,可以選擇時間戳字段作為分表字段,以便按天、按月或按年分割數(shù)據(jù),方便查詢和維護。
-
地理信息字段:如果業(yè)務需要按地區(qū)查詢數(shù)據(jù),那么選擇地理信息字段作為分表字段是合適的。例如,在訂單表中,可以選擇訂單地區(qū)字段作為分表字段,以便將訂單數(shù)據(jù)按地區(qū)進行拆分,方便查詢和擴展。
-
關聯(lián)字段:如果業(yè)務需要頻繁進行關聯(lián)查詢,那么選擇訂單號等關聯(lián)字段作為分表字段。例如,在訂單表中,可以選擇訂單號作為分表字段,因為訂單號唯一且包含業(yè)務信息,并且日常查詢、關聯(lián)查詢都是根據(jù)訂單號查詢的,很少根據(jù)id查詢,方便查詢和維護。
選擇分表字段的原則:
1. 數(shù)據(jù)分布均勻
最佳的分表字段應該是能夠讓數(shù)據(jù)分布均勻的字段,這樣可以避免某個表的數(shù)據(jù)過多,導致查詢效率降低。在用戶表中,如果以地區(qū)作為分表字段,可能會導致某些地區(qū)的數(shù)據(jù)過多,而某些地區(qū)的數(shù)據(jù)過少。
2. 頻繁查詢的字段
盡量選擇查詢頻率最高的字段(例如主鍵id),然后根據(jù)表拆分方式選擇字段。在一個訂單表中,如果經(jīng)常需要根據(jù)用戶ID查詢訂單信息,那么以用戶ID作為分表字段是一個不錯的選擇。
3. 不可變字段
最佳的分表字段還應該是不可變的字段,這樣可以避免在數(shù)據(jù)遷移時出現(xiàn)問題。在一個商品表中,如果選擇以商品名稱作為分表字段,那么當商品名稱發(fā)生變化時,就需要將數(shù)據(jù)移動到不同的表中,這樣會增加系統(tǒng)的復雜度。
3.5 代碼改造
修改代碼里的查詢、更新語句,以便讓其適應分庫分表后的情況。
查詢語句改造:
-
單庫查詢改為跨庫查詢:對于需要查詢的字段,需要明確指定查詢的庫和表,以避免查詢到錯誤的數(shù)據(jù)。例如,原來的查詢語句 “SELECT * FROM users WHERE id = 1” 可以修改為 “SELECT * FROM db.table_name WHERE id = 1”,其中 db 為目標數(shù)據(jù)庫,table_name 為目標表。
-
單表查詢改為跨表查詢:例如投訴記錄表根據(jù)哈希取余的方式分成10個表,如果id%1=0,則查0號表complaint_records_0。
3.6 數(shù)據(jù)遷移
最簡單的就是停機遷移,復雜點的就是不停機遷移,要考慮增量同步和全量同步的問題。
3.6.1 增量同步
增量同步:老庫遷移到新庫期間,增刪改命令的落庫不能出錯
- 同步雙寫:同步寫新庫和老庫;
- 異步雙寫(推薦): 寫老庫,監(jiān)聽binlog異步同步到新庫
- 中間件同步工具:通過一定的規(guī)則將數(shù)據(jù)同步到目標庫表
3.6.2 全量同步
全量同步:老庫到新庫的數(shù)據(jù)遷移,要控制好遷移效率,解決增量數(shù)據(jù)的一致性。
- 定時任務查老庫寫新庫
- 使用中間件遷移數(shù)據(jù),例如Dbmate、Apache NiFi、Ladder、Phinx、Flyway、TiDB等。
3.7 數(shù)據(jù)一致性校驗和補償
假設采用異步雙寫方案,在遷移完成后,逐條對比新老庫數(shù)據(jù),一致則跳過,不一致則補償:
- 新庫存在,老庫不存在:新庫刪除數(shù)據(jù)
- 新庫不存在,老庫存在:新庫插入數(shù)據(jù)
- 新庫存在、老庫存在:比較所有字段,不一致則將新庫更新為老庫數(shù)據(jù)
3.8 灰度切讀
灰度發(fā)布:指黑(舊版本)與白(新版本)之間,讓一些用戶繼續(xù)用舊版本,一些用戶開始用新版本,如果用戶對新版本沒什么意見,就逐步把所有用戶遷移到新版本,實現(xiàn)平滑過渡發(fā)布。
原則:文章來源:http://www.zghlxwxcb.cn/news/detail-770812.html
- 有問題及時切回老庫
- 灰度放量先慢后快,每次放量觀察一段時間
- 支持靈活的規(guī)則:門店維度灰度、百 (萬)分比灰度
3.9 停舊庫、寫新庫
下線老庫,用新庫讀寫。文章來源地址http://www.zghlxwxcb.cn/news/detail-770812.html
到了這里,關于一篇文章搞懂MySQL的分庫分表,從拆分場景、目標評估、拆分方案、不停機遷移、一致性補償?shù)确矫嬖敿氷U述MySQL數(shù)據(jù)庫的分庫分表方案的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!