程序員分庫(kù)分表:入門(mén)必備
大家好,我是猿哥,今天給大家分享分庫(kù)分表系列的第1篇:入門(mén)篇
當(dāng)一張表的數(shù)據(jù)達(dá)到幾千萬(wàn)時(shí),查詢一次所花的時(shí)間會(huì)變長(zhǎng)。業(yè)界公認(rèn)MySQL單表容量在 1千萬(wàn) 以下是最佳狀態(tài),因?yàn)檫@時(shí)它的BTREE索引樹(shù)高在3~5之間。
這只是業(yè)界這么說(shuō),最終具體還得看表結(jié)構(gòu)設(shè)計(jì)以及硬件情況。
一、數(shù)據(jù)庫(kù)瓶頸
不管是IO瓶頸,還是CPU瓶頸,最終都會(huì)導(dǎo)致數(shù)據(jù)庫(kù)的活躍連接數(shù)增加,進(jìn)而逼近甚至達(dá)到數(shù)據(jù)庫(kù)可承載活躍連接數(shù)的閾值。在業(yè)務(wù)Service來(lái)看就是,可用數(shù)據(jù)庫(kù)連接少甚至無(wú)連接可用。接下來(lái)就可以想象了吧(并發(fā)量、吞吐量、崩潰)。
1、IO瓶頸
第一種:磁盤(pán)讀IO瓶頸,熱點(diǎn)數(shù)據(jù)太多,數(shù)據(jù)庫(kù)緩存放不下,每次查詢時(shí)會(huì)產(chǎn)生大量的IO,降低查詢速度 -> 分庫(kù)和垂直分表。
第二種:網(wǎng)絡(luò)IO瓶頸,請(qǐng)求的數(shù)據(jù)太多,網(wǎng)絡(luò)帶寬不夠 -> 分庫(kù)。
2、CPU瓶頸
第一種:SQL問(wèn)題,如SQL中包含join,group by,order by,非索引字段條件查詢等,增加CPU運(yùn)算的操作 -> SQL優(yōu)化,建立合適的索引,在業(yè)務(wù)Service層進(jìn)行業(yè)務(wù)計(jì)算。
第二種:?jiǎn)伪頂?shù)據(jù)量太大,查詢時(shí)掃描的行太多,SQL效率低,CPU率先出現(xiàn)瓶頸 -> 水平分表。
數(shù)據(jù)切分可以分為:垂直切分和水平切分。
二、垂直切分
垂直切分又可以分為: 垂直分庫(kù)和垂直分表。
1、垂直分庫(kù)
概念 就是根據(jù)業(yè)務(wù)耦合性,將關(guān)聯(lián)度低的不同表存儲(chǔ)在不同的數(shù)據(jù)庫(kù)。做法與大系統(tǒng)拆分為多個(gè)小系統(tǒng)類似,按業(yè)務(wù)分類進(jìn)行獨(dú)立劃分。與"微服務(wù)治理"的做法相似,
每個(gè)微服務(wù)使用單獨(dú)的一個(gè)數(shù)據(jù)庫(kù)。
如圖:

說(shuō)明
一開(kāi)始我們是單體服務(wù),所以只有一個(gè)數(shù)據(jù)庫(kù),所有的表都在這個(gè)庫(kù)里。
后來(lái)因?yàn)闃I(yè)務(wù)需求,單體服務(wù)變成微服務(wù)治理。所以將之前的一個(gè)商品庫(kù),拆分成多個(gè)數(shù)據(jù)庫(kù)。每個(gè)微服務(wù)對(duì)于一個(gè)數(shù)據(jù)庫(kù)。
2、垂直分表
概念 把一個(gè)表的多個(gè)字段分別拆成多個(gè)表,一般按字段的冷熱拆分,熱字段一個(gè)表,冷字段一個(gè)表。從而提升了數(shù)據(jù)庫(kù)性能。
如圖:

說(shuō)明
一開(kāi)始商品表中包含商品的所有字段,但是我們發(fā)現(xiàn):
1.商品詳情和商品屬性字段較長(zhǎng)。2.商品列表的時(shí)候我們是不需要顯示商品詳情和商品屬性信息,只有在點(diǎn)進(jìn)商品商品的時(shí)候才會(huì)展示商品詳情信息。
所以可以考慮把商品詳情和商品屬性單獨(dú)切分一張表,提高查詢效率。
3、垂直切分優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
-?解決業(yè)務(wù)系統(tǒng)層面的耦合,業(yè)務(wù)清晰
-?與微服務(wù)的治理類似,也能對(duì)不同業(yè)務(wù)的數(shù)據(jù)進(jìn)行分級(jí)管理、維護(hù)、監(jiān)控、擴(kuò)展等
-?高并發(fā)場(chǎng)景下,垂直切分一定程度的提升IO、數(shù)據(jù)庫(kù)連接數(shù)、單機(jī)硬件資源的瓶頸
缺點(diǎn)
-?分庫(kù)后無(wú)法Join,只能通過(guò)接口聚合方式解決,提升了開(kāi)發(fā)的復(fù)雜度
-?分庫(kù)后分布式事務(wù)處理復(fù)雜
-?依然存在單表數(shù)據(jù)量過(guò)大的問(wèn)題(需要水平切分)
二、水平切分
當(dāng)一個(gè)應(yīng)用難以再細(xì)粒度的垂直切分或切分后數(shù)據(jù)量行數(shù)巨大,存在單庫(kù)讀寫(xiě)、存儲(chǔ)性能瓶頸,這時(shí)候就需要進(jìn)行水平切分了。
水平切分也可以分為:水平分庫(kù)和水平分表。
1、水平分庫(kù)
水平分庫(kù)的原因
上面雖然已經(jīng)把商品庫(kù)分成3個(gè)庫(kù),但是隨著業(yè)務(wù)的增加一個(gè)訂單庫(kù)也出現(xiàn)QPS過(guò)高,數(shù)據(jù)庫(kù)響應(yīng)速度來(lái)不及,一般mysql單機(jī)也就1000左右的QPS,如果超過(guò)1000就要考慮分庫(kù)。
如圖

2、水平分表
概念 一般我們一張表的數(shù)據(jù)不要超過(guò)1千萬(wàn),如果表數(shù)據(jù)超過(guò)1千萬(wàn),并且還在不斷增加數(shù)據(jù),那就可以考慮分表。
如圖

3、水平切分優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
-?不存在單庫(kù)數(shù)據(jù)量過(guò)大、高并發(fā)的性能瓶頸,提升系統(tǒng)穩(wěn)定性和負(fù)載能力
-?應(yīng)用端改造較小,不需要拆分業(yè)務(wù)模塊
缺點(diǎn)
-?跨分片的事務(wù)一致性難以保證
-?跨庫(kù)的Join關(guān)聯(lián)查詢性能較差
-?數(shù)據(jù)多次擴(kuò)展難度和維護(hù)量極大
三、數(shù)據(jù)分片規(guī)則
我們考慮去使用水平切分表,將一張表水平切分成多張表,這就涉及到數(shù)據(jù)分片的規(guī)則,比較常見(jiàn)的有:Hash取模分表、數(shù)值Range分表、一致性Hash算法分表。
1、Hash取模分表
概念 一般采用Hash取模的切分方式,例如:假設(shè)按goods_id分4張表。(goods_id%4 取整確定表)

優(yōu)點(diǎn)
?-?數(shù)據(jù)分片相對(duì)比較均勻,不容易出現(xiàn)熱點(diǎn)和并發(fā)訪問(wèn)的瓶頸。
缺點(diǎn)
-?后期分片集群擴(kuò)容時(shí),需要遷移舊的數(shù)據(jù)很難。
-?容易面臨跨分片查詢的復(fù)雜問(wèn)題。比如上例中,如果頻繁用到的查詢條件中不帶goods_id時(shí),將會(huì)導(dǎo)致無(wú)法定位數(shù)據(jù)庫(kù),從而需要同時(shí)向4個(gè)庫(kù)發(fā)起查詢,
再在內(nèi)存中合并數(shù)據(jù),取最小集返回給應(yīng)用,分庫(kù)反而成為拖累。
2、數(shù)值Range分表
概念 按照時(shí)間區(qū)間或ID區(qū)間來(lái)切分。例如:將goods_id為11000的記錄分到第一個(gè)表,10012000的分到第二個(gè)表,以此類推。
如圖

優(yōu)點(diǎn)
-?單表大小可控
-?天然便于水平擴(kuò)展,后期如果想對(duì)整個(gè)分片集群擴(kuò)容時(shí),只需要添加節(jié)點(diǎn)即可,無(wú)需對(duì)其他分片的數(shù)據(jù)進(jìn)行遷移
-?使用分片字段進(jìn)行范圍查找時(shí),連續(xù)分片可快速定位分片進(jìn)行快速查詢,有效避免跨分片查詢的問(wèn)題。
缺點(diǎn)
-?熱點(diǎn)數(shù)據(jù)成為性能瓶頸。
例如按時(shí)間字段分片,有些分片存儲(chǔ)最近時(shí)間段內(nèi)的數(shù)據(jù),可能會(huì)被頻繁的讀寫(xiě),而有些分片存儲(chǔ)的歷史數(shù)據(jù),則很少被查詢
3、一致性Hash算法
一致性Hash算法能很好的解決因?yàn)镠ash取模而產(chǎn)生的分片集群擴(kuò)容時(shí),需要遷移舊的數(shù)據(jù)的難題。至于具體原理這里就不詳細(xì)說(shuō),
四、分庫(kù)分表帶來(lái)的問(wèn)題
任何事情都有兩面性,分庫(kù)分表也不例外,如果采用分庫(kù)分表,會(huì)引入新的的問(wèn)題
1、分布式事務(wù)問(wèn)題
使用分布式事務(wù)中間件解決,具體是通過(guò)最終一致性還是強(qiáng)一致性分布式事務(wù),看業(yè)務(wù)需求,這里就不多說(shuō)。
2、跨節(jié)點(diǎn)關(guān)聯(lián)查詢 Join 問(wèn)題
切分之前,我們可以通過(guò)Join來(lái)完成。而切分之后,數(shù)據(jù)可能分布在不同的節(jié)點(diǎn)上,此時(shí)Join帶來(lái)的問(wèn)題就比較麻煩了,考慮到性能,盡量避免使用Join查詢。
解決這個(gè)問(wèn)題的一些方法:
全局表
全局表,也可看做是 "數(shù)據(jù)字典表",就是系統(tǒng)中所有模塊都可能依賴的一些表,為了避免跨庫(kù)Join查詢,可以將 這類表在每個(gè)數(shù)據(jù)庫(kù)中都保存一份。這些數(shù)據(jù)通常
很少會(huì)進(jìn)行修改,所以也不擔(dān)心一致性的問(wèn)題。
字段冗余
利用空間換時(shí)間,為了性能而避免join查詢。例:訂單表保存userId時(shí)候,也將userName冗余保存一份,這樣查詢訂單詳情時(shí)就不需要再去查詢"買家user表"了。
數(shù)據(jù)組裝
在系統(tǒng)層面,分兩次查詢。第一次查詢的結(jié)果集中找出關(guān)聯(lián)數(shù)據(jù)id,然后根據(jù)id發(fā)起第二次請(qǐng)求得到關(guān)聯(lián)數(shù)據(jù)。最后將獲得到的數(shù)據(jù)進(jìn)行字段拼裝。
3、跨節(jié)點(diǎn)分頁(yè)、排序、函數(shù)問(wèn)題
跨節(jié)點(diǎn)多庫(kù)進(jìn)行查詢時(shí),會(huì)出現(xiàn)Limit分頁(yè)、Order by排序等問(wèn)題。分頁(yè)需要按照指定字段進(jìn)行排序,當(dāng)排序字段就是分片字段時(shí),通過(guò)分片規(guī)則就比較容易定位到指定的分片;
當(dāng)排序字段非分片字段時(shí),就變得比較復(fù)雜了。需要先在不同的分片節(jié)點(diǎn)中將數(shù)據(jù)進(jìn)行排序并返回,然后將不同分片返回的結(jié)果集進(jìn)行匯總和再次排序,最終返回給用戶。
4、全局主鍵避重問(wèn)題
如果都用主鍵自增肯定不合理,如果用UUID那么無(wú)法做到根據(jù)主鍵排序,所以我們可以考慮通過(guò)雪花ID來(lái)作為數(shù)據(jù)庫(kù)的主鍵。
5、數(shù)據(jù)遷移問(wèn)題
采用雙寫(xiě)的方式,修改代碼,所有涉及到分庫(kù)分表的表的增、刪、改的代碼,都要對(duì)新庫(kù)進(jìn)行增刪改。同時(shí),再有一個(gè)數(shù)據(jù)抽取服務(wù),不斷地從老庫(kù)抽數(shù)據(jù),往新庫(kù)寫(xiě),
邊寫(xiě)邊按時(shí)間比較數(shù)據(jù)是不是最新的
?關(guān)注公眾號(hào):Java后端編程,回復(fù)下面關(guān)鍵字?
要Java學(xué)習(xí)完整路線,回復(fù)??路線?
缺Java入門(mén)視頻,回復(fù):?視頻?
要Java面試經(jīng)驗(yàn),回復(fù)??面試?
缺Java項(xiàng)目,回復(fù):?項(xiàng)目?
進(jìn)Java粉絲群:?加群?
PS:如果覺(jué)得我的分享不錯(cuò),歡迎大家隨手點(diǎn)贊、在看。
(完) 加我"微信"?獲取一份 最新Java面試題資料 請(qǐng)備注:666,不然不通過(guò)~
最近好文
1、Kafka 3.0重磅發(fā)布,棄用 Java 8 的支持!
最近面試BAT,整理一份面試資料《Java面試BAT通關(guān)手冊(cè)》,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫(kù)、數(shù)據(jù)結(jié)構(gòu)等等。 獲取方式:關(guān)注公眾號(hào)并回復(fù)?java?領(lǐng)取,更多內(nèi)容陸續(xù)奉上。 明天見(jiàn)(??ω??)??
