面試的時候,聊到高并發(fā)或者大數(shù)據(jù),很多時候會聊到數(shù)據(jù)庫分庫分表相關(guān)的問題,因為你的數(shù)據(jù)庫單機(jī)抗不了多少并發(fā)量,而且用戶量上來之后,數(shù)據(jù)庫容納的數(shù)據(jù)量也是有限的。
如果單表數(shù)據(jù)量過大,SQL稍微復(fù)雜點,查詢就會很慢。而且,現(xiàn)在稍微大點的互聯(lián)網(wǎng)公司,分庫分表都成為了標(biāo)配。如果你現(xiàn)在出去面試,面試官問你分庫分表相關(guān)的問題,你說你沒做過,人家立馬會覺得你沒有高并發(fā)的經(jīng)驗,做的都是比較簡單的業(yè)務(wù)系統(tǒng)。
MySQL單表通常500w條數(shù)據(jù)以內(nèi)比較合適,不建議超過1000w,如果超過1000w了建議要做分庫分表了。
比如常見的分庫分表面試題:
- 你們具體是如何對數(shù)據(jù)庫進(jìn)行進(jìn)行垂直拆分或水平拆分的?
- 你們是如何把系統(tǒng)不停機(jī)遷移到分庫分表的?
一般開發(fā)一個新業(yè)務(wù)系統(tǒng),由于需要快速打樣,盡快上線,所以一開始基本都是單庫系統(tǒng)。可能業(yè)務(wù)發(fā)展迅猛,過了幾個月,使用用戶就達(dá)到了 1000 萬!每天活躍用戶數(shù) 100 萬!每天單表數(shù)據(jù)量 10 萬條!高峰期每秒最大請求達(dá)到 1000QPS!
現(xiàn)在大家感覺壓力已經(jīng)有點大了,為啥呢?因為每天多 10 萬條數(shù)據(jù),一個月就新增300 萬條數(shù)據(jù),現(xiàn)在咱們單表已經(jīng)幾百萬數(shù)據(jù)了,馬上就破千萬了。目前用戶量還在不斷增長,每天新增的數(shù)據(jù)量也在不斷變多,照目前這個勢頭,系統(tǒng)恐怕堅持不了多久。業(yè)務(wù)系統(tǒng)倒是可以很容易的增加一些機(jī)器。圖2?業(yè)務(wù)系統(tǒng)增加機(jī)器但大多數(shù)請求集中在20%的時間,80%的時間請求量還可以支撐。這20%時間段里,每秒并發(fā)量和在線用戶量都達(dá)到峰值,對數(shù)據(jù)庫的壓力也是每天最大的時候。這時候,你可以在業(yè)務(wù)系統(tǒng)與MySQL數(shù)據(jù)庫中間加一個MQ削峰,比如使用kafka,緩解一下過高的并發(fā)請求。假如高峰期每秒8000個請求,異步寫系統(tǒng)每秒消費(fèi)2000個請求。經(jīng)過MQ削峰后,會在消息隊列里緩存很多未執(zhí)行的數(shù)據(jù)庫操作,等待異步寫系統(tǒng)慢慢的消費(fèi)掉。業(yè)務(wù)系統(tǒng)能夠增加機(jī)器擴(kuò)容,沒什么問題,MQ削峰也能撐一撐,但瓶頸在于MySQL。主要有3個問題:如果要讓MySQL承擔(dān)更高的并發(fā),比如現(xiàn)在是8000請求/s,異步寫系統(tǒng)也可以擴(kuò)容到多臺機(jī)器,MQ消費(fèi)6000請求/s,這時候該怎么辦?你首先得分庫。假如現(xiàn)在分了3個庫,每個庫的表和表結(jié)構(gòu)都是一模一樣的,MQ分為3個partition,每個異步寫系統(tǒng)都只消費(fèi)一個partition。每個異步寫系統(tǒng),會根據(jù)每條數(shù)據(jù)的某個id分發(fā)到各個數(shù)據(jù)庫里去,比如是userId,每個userId相同的數(shù)據(jù)分發(fā)到同一臺機(jī)器上去。
分庫前單庫每天可能增加100w數(shù)據(jù),現(xiàn)在每個庫增加30多萬條數(shù)據(jù)。數(shù)據(jù)庫可以承受的并發(fā)增加了3倍,數(shù)據(jù)庫的磁盤使用率大大降低,本來一個庫磁盤很快就寫滿了,現(xiàn)在大大降低了,同時SQL語句執(zhí)行性能也提高了。分庫之后,每個表的數(shù)據(jù)庫依舊很多,SQL語句執(zhí)行起來性能依舊不高,所以還是要考慮分表,打造多庫多表的系統(tǒng)。
千萬不能因為技術(shù)原因制約了公司業(yè)務(wù)的發(fā)展。比如你單表都幾千萬數(shù)據(jù)了,你確定你能扛住么?肯定不行,單表數(shù)據(jù)量太大,會極大影響你的SQL執(zhí)行的性能,大量的連接卡在MySQL等待執(zhí)行,不僅會把你MySQL數(shù)據(jù)庫拖垮,還會產(chǎn)生連鎖反應(yīng),把你的業(yè)務(wù)系拖垮。一般來說,單表到幾百萬的時候,性能就會越來越差,你就得分表了。就是把一個表的數(shù)據(jù)放到多個表中,然后查詢的時候你就查一個表。比如按照用戶 id 來分表,將一個用戶的數(shù)據(jù)就放在一個表中。然后操作的時候你對一個用戶就操作那個表就好了。這樣可以控制每個表的數(shù)據(jù)量在可控的范圍內(nèi),比如每個表就固定在500萬以內(nèi)。分庫是啥意思?就經(jīng)驗而言,單庫最多支撐到并發(fā)2000,一定要擴(kuò)容了,而且一個健康的單庫并發(fā)值最好保持在每秒1000以內(nèi),不要太大。那么你可以將一個庫的數(shù)據(jù)拆分到多個庫中,訪問的時候就訪問一個庫好了。
- ShardingSphere?(Sharding-jdbc)
Sharding-Sphere是一套開源的分布式數(shù)據(jù)庫中間件解決方案,屬于client端方案,也就是你的業(yè)務(wù)系統(tǒng)只需要引用它的jar包,就可以使用了。Sharding-Sphere目前社區(qū)也還一直在開發(fā)和維護(hù),還算是比較活躍,個人認(rèn)為算是一個現(xiàn)在也可以選擇的方案。mycat是基于Cobar改造的,屬于 proxy 層方案,支持的功能非常完善,而且目前應(yīng)該是非常火的而且不斷流行的數(shù)據(jù)庫中間件,社區(qū)很活躍,也有一些公司開始在用了。但是確實相比于 Sharding jdbc 來說,年輕一些,經(jīng)歷的錘煉少一些。Sharding-Sphere這種client端方案的優(yōu)點在于不用部署,運(yùn)維成本低,不需要代理層的二次轉(zhuǎn)發(fā)請求,性能很高,但是如果遇到升級啥的需要各個業(yè)務(wù)系統(tǒng)都重新升級版本再發(fā)布,各個系統(tǒng)都需要耦合Sharding-Sphere即可。Mycat 這種proxy層方案的缺點在于需要單獨(dú)部署,自己運(yùn)維一套中間件,運(yùn)維成本高,但是好處在于對于各個項目是透明的,如果需要升級只需要單獨(dú)升級mycat就行了。通常來說,這兩個方案其實都可以選用,但是個人建議中小型公司選用 Sharding-Sphere,client 層方案輕量級,而且維護(hù)成本低,不需要額外增派人手去維護(hù),而且中小型公司系統(tǒng)復(fù)雜度會低一些,項目也沒那么多。但是中大型公司最好還是選用Mycat這類proxy層方案,因為可能大公司系統(tǒng)和項目非常多,團(tuán)隊很大,人員充足,那么最好是專門弄個人來研究和維護(hù)Mycat,然后各個項目直接透明使用即可。
你們是如何對數(shù)據(jù)庫進(jìn)行垂直拆分或水平拆分的?
水平拆分的意思,就是把一個表的數(shù)據(jù)給弄到多個庫的多個表里去,但是每個庫的表結(jié)構(gòu)都一樣,只不過每個庫表放的數(shù)據(jù)是不同的,所有庫表的數(shù)據(jù)加起來就是全部數(shù)據(jù)。水平拆分的意義,就是將數(shù)據(jù)均勻放更多的庫里,然后用多個庫來扛更高的并發(fā),還有就是用多個庫的存儲容量來進(jìn)行擴(kuò)容。垂直拆分,就是把一個有很多字段的表給拆分成多個表,或者是多個庫上去。每個庫表的結(jié)構(gòu)都不一樣,每個庫表都包含部分字段。一般來說,會將訪問頻率很高的字段放到一個表里去,然后將訪問頻率很低的字段放到另外一個表里去。因為數(shù)據(jù)庫是有緩存的,你訪問頻率高的行字段越少,就可以在緩存里緩存更多的行,性能就越好。這個一般在表層面做的較多一些。假如有600w數(shù)據(jù),現(xiàn)在要分庫分表,綜合來看分庫分表可能是這樣的:常見的分庫分表是,是根據(jù)某個id取模先定位到庫,再定位到表的。可以根據(jù)userId和orderId取模。也可以根據(jù)數(shù)據(jù)的range去分庫分表,比如根據(jù)數(shù)據(jù)的創(chuàng)建時間。引入了分庫分表中間件,那么我們的系統(tǒng)就不用自己考慮每條數(shù)據(jù)路由到哪個庫哪張表了。就可以直接將SQL丟給分庫分表中間件,由它根據(jù)配置,路由到相應(yīng)的庫和表里去,此時MQ和異步寫系統(tǒng)也可以去掉了,因為分庫分表后,相當(dāng)于每個業(yè)務(wù)系統(tǒng)承擔(dān)的壓力就大大減小了。
本文分享了分庫分表的由來,業(yè)務(wù)不斷發(fā)展的驅(qū)動下,改造系統(tǒng)分庫分表是架構(gòu)升級的必經(jīng)之路。同時講了業(yè)內(nèi)常用的分庫分表中間件ShardingSphere和Mycat以及他們的優(yōu)缺點,最后分享了如何對數(shù)據(jù)庫進(jìn)行垂直拆分和水平拆分,以及具體分庫分表數(shù)據(jù)路由方法。
有道無術(shù),術(shù)可成;有術(shù)無道,止于術(shù)
歡迎大家關(guān)注Java之道公眾號
好文章,我在看??