大家好,我是不才陳某~
陳某的知識(shí)星球開通了,一個(gè)相互交流的技術(shù)圈子,陳某會(huì)在星球中定期分享干貨,如果你也想和球友一起打卡學(xué)習(xí)進(jìn)階,戳鏈接加入
在前面的文章中探討了架構(gòu)優(yōu)化的兩種方案:冷熱分離、查詢分離
查詢分離其實(shí)就是利用了非關(guān)系數(shù)據(jù)庫的高性能,但是不足之處也很明顯:當(dāng)主數(shù)據(jù)量越來越多,寫操作緩慢;這種問題如何破局?可見任何一種優(yōu)化方案都不是最終的銀彈,只有不斷的優(yōu)化演變
這篇文章就來介紹一下解決方案:分庫分表,將圍繞以下幾點(diǎn)介紹:
在介紹選型之前先來介紹下架構(gòu)背景,筆者曾經(jīng)做過電商系統(tǒng)的優(yōu)化,該系統(tǒng)中包含的兩個(gè)主體:
對(duì)于如此量級(jí)的數(shù)據(jù),單庫單表的情況下,無論是IO還是CPU都扛不住,架構(gòu)上的優(yōu)化是必然。
經(jīng)過了多次探討嘗試,最終選擇了分庫分表。
說到分庫分表首先想到的就是存儲(chǔ)選型,關(guān)于持久層的選型主流的無非有如下幾種:
關(guān)系型數(shù)據(jù)庫目前市面上主流無非三種:MySQL、Oracle、SqlServer,筆者更傾向于MySQL,也是很多新型企業(yè)在用的一種數(shù)據(jù)庫,因此本篇文章也將重點(diǎn)圍繞MySQL展開
在任何系統(tǒng)中關(guān)系型數(shù)據(jù)庫的地位都是不可或缺的,它的強(qiáng)約束性、事務(wù)的控制、SQL語法、鎖....這些功能可謂是久經(jīng)考驗(yàn),因此在功能上 MySQL 能滿足我們所有的業(yè)務(wù)需求。
說到NoSQL,第一個(gè)想到就是MongoDB ,它的分片功能從并發(fā)性和數(shù)據(jù)量這兩個(gè)角度已經(jīng)能滿足一般大數(shù)據(jù)量的需求,但是仍然需要考慮如下幾點(diǎn):
NewSQL目前比較主流則是TiDB,該技術(shù)比較新,雖然能夠滿足大數(shù)據(jù)量的存儲(chǔ),但是在選擇上還是需要做些考量:
什么是分表分庫?分表是將一份大的表數(shù)據(jù)拆分存放至多個(gè)結(jié)構(gòu)一樣的拆分表;分庫就是將一個(gè)大的數(shù)據(jù)庫拆分成多個(gè)結(jié)構(gòu)一樣的小庫。
前面介紹的三種拆分存儲(chǔ)技術(shù),在我以往的項(xiàng)目中我都沒使用過,而是選擇了基于 MySQL 的分表分庫,主要是有一個(gè)重要考量:分表分庫對(duì)于第三方依賴較少,業(yè)務(wù)邏輯靈活可控,它本身并不需要非常復(fù)雜的底層處理,也不需要重新做數(shù)據(jù)庫,只是根據(jù)不同邏輯使用不同 SQL 語句和數(shù)據(jù)源而已。
目前市面上主流的分庫分表分為兩種模式:Proxy模式、Client模式
Proxy模式屬于業(yè)務(wù)無侵入型,直接代理數(shù)據(jù)庫,對(duì)于開發(fā)者一切都是無感知的,SQL 組合、數(shù)據(jù)庫路由、執(zhí)行結(jié)果合并等功能全部存放在一個(gè)代理服務(wù)中,比如MyCat
、ShardingSphere
都對(duì)Proxy模式提供了支持
Client模式屬于業(yè)務(wù)侵入型,將分庫分表的邏輯放在客戶端,客戶端需要引入一個(gè)jar,比如Sharding-JDBC,架構(gòu)圖如下:
市面上對(duì)于分庫分表中間件如下:
兩種模式的優(yōu)缺點(diǎn)也很明顯:
在落實(shí)分表分庫解決方案時(shí),我們需要考慮 5 個(gè)要點(diǎn)。
針對(duì)訂單這個(gè)業(yè)務(wù),其中涉及到以下幾個(gè)主要的字段:
user_id
:用戶idorder_id
:訂單idorder_time
:下單時(shí)間store_id
:店鋪id經(jīng)過考量,最終選擇了user_id
作為ShardingKey,為什么呢?
選擇user_id作為ShardingKey需要結(jié)合業(yè)務(wù)場(chǎng)景,訂單系統(tǒng)中常見的業(yè)務(wù):
以上三種業(yè)務(wù)場(chǎng)景,判斷下優(yōu)先級(jí),C端用戶肯定是需要優(yōu)先滿足,因此使用user_id作為ShardingKey
這樣在查詢時(shí)需要將user_id傳遞過來才能定位到指定庫、表
選擇字段作為分片鍵時(shí),我們一般需要考慮三點(diǎn)要求:數(shù)據(jù)盡量均勻分布在不同表或庫、跨庫查詢操作盡可能少、這個(gè)字段的值不會(huì)變(這點(diǎn)尤為重要)。
選擇user_id作為ShardingKey之后,需要考慮使用分片策略了,主要分為如下三種
1、范圍分片
假如user_id是自增的數(shù)字,則可以根據(jù)user_id范圍進(jìn)行分片,每100萬份分為一個(gè)庫,每10萬份分為一個(gè)表,此時(shí)單個(gè)庫中將分為10張表,如下表:
2、Hash取模
這種方案是根據(jù)Hash值進(jìn)行分片,比如Hash函數(shù)為:hash(user_id%8)
,這里是將user_id對(duì)8這個(gè)特定值取模,最終分為了8張表;這里一般為了方便后續(xù)擴(kuò)容,建議選擇2的N次方
3、范圍分片和Hash取模混合
比如先按照范圍對(duì)user_id拆分,每100萬份分為一個(gè)庫,在對(duì)這100萬份數(shù)據(jù)進(jìn)行Hash取模(hash(user_id%8)
)拆分成8個(gè)表
當(dāng)然以上三種方案的優(yōu)缺點(diǎn)也是非常明顯,這里不再贅述了
需要注意的是:在拆分之前,為了避免頻繁的擴(kuò)容,一定要對(duì)未來5年或者10年數(shù)據(jù)增長做個(gè)判斷,預(yù)留更多的分片
業(yè)務(wù)代碼的修改這里就不好說了,和自身的業(yè)務(wù)是強(qiáng)關(guān)聯(lián)。
但是,在這里我想分享一些個(gè)人觀點(diǎn)。近年來,分表分庫操作愈發(fā)容易,不過我們需要注意幾個(gè)要點(diǎn)。
我們已經(jīng)習(xí)慣微服務(wù)了,對(duì)于特定表的分表分庫,其影響面只在該表所在的服務(wù)中,如果是一個(gè)單體架構(gòu)的應(yīng)用做分表分庫,那真是傷腦筋。
在互聯(lián)網(wǎng)架構(gòu)中,我們基本不使用外鍵約束。
隨著查詢分離的流行,后臺(tái)系統(tǒng)中有很多操作需要跨庫查詢,導(dǎo)致系統(tǒng)性能非常差,這時(shí)分表分庫一般會(huì)結(jié)合查詢分離一起操作:先將所有數(shù)據(jù)在 ES 索引一份,再使用 ES 在后臺(tái)直接查詢數(shù)據(jù)。如果訂單詳情數(shù)據(jù)量很大,還有一個(gè)常見做法,即先在 ES 中存儲(chǔ)索引字段(作為查詢條件的字段),再將詳情數(shù)據(jù)存放在 HBase 中(這個(gè)方案我們就不展開了)。
歷史數(shù)據(jù)的遷移非常耗時(shí),有時(shí)遷移幾天幾夜都很正常。而在互聯(lián)網(wǎng)行業(yè)中,別說幾天幾夜了,就連停機(jī)幾分鐘業(yè)務(wù)都無法接受,這就要求我們給出一個(gè)無縫遷移的解決方案。
還記得講解查詢分離時(shí),我們說過的方案嗎?我們?cè)賮砘仡櫹拢缦聢D所示:
歷史數(shù)據(jù)遷移時(shí),我們就是采用類似的方案進(jìn)行歷史數(shù)據(jù)遷移,如下圖所示:
此數(shù)據(jù)遷移方案的基本思路:存量數(shù)據(jù)直接遷移,增量數(shù)據(jù)監(jiān)聽 binlog,然后通過 canal 通知遷移程序搬運(yùn)數(shù)據(jù),新的數(shù)據(jù)庫擁有全量數(shù)據(jù),且校驗(yàn)通過后逐步切換流量。
數(shù)據(jù)遷移解決方案詳細(xì)的步驟如下:
上線 canal,通過 canal 觸發(fā)增量數(shù)據(jù)的遷移;
遷移數(shù)據(jù)腳本測(cè)試通過后,將老數(shù)據(jù)遷移到新的分表分庫中;
注意遷移增量數(shù)據(jù)與遷移老數(shù)據(jù)的時(shí)間差,確保全部數(shù)據(jù)都被遷移過去,無遺漏;
第二步、第三步都運(yùn)行完后,新的分表分庫中已經(jīng)擁有全量數(shù)據(jù)了,這時(shí)我們可以運(yùn)行數(shù)據(jù)驗(yàn)證的程序,確保所有數(shù)據(jù)都存放在新數(shù)據(jù)庫中;
到這步數(shù)據(jù)遷移就算完成了,之后就是新版本代碼上線了,至于是灰度上還是直接上,需要根據(jù)你們的實(shí)際情況決定,回滾方案也是一樣。
隨著業(yè)務(wù)的發(fā)展,如果原來的分片設(shè)計(jì)已經(jīng)無法滿足日益增長的數(shù)據(jù)需求,我們就需要考慮擴(kuò)容了,擴(kuò)容方案主要依賴以下兩點(diǎn)。
分片策略是否可以讓新表數(shù)據(jù)的遷移源只是 1 個(gè)舊表,而不是多個(gè)舊表,這就是前面我們建議使用 2 的 N 次方分表的原因;
數(shù)據(jù)遷移:我們需要把舊分片的數(shù)據(jù)遷移到新的分片上,這個(gè)方案與上面提及的歷史數(shù)據(jù)遷移一樣,我們就不重復(fù)啰唆了。
分表分庫的解決方案講完了,以上就是業(yè)界常用的一些做法,不過此方案仍然存在不足之處。
歡迎加入陳某的知識(shí)星球,一起學(xué)習(xí)打卡,交流技術(shù)。加入方式,掃描下方二維碼:
已在知識(shí)星球中更新如下幾個(gè)專欄,詳情戳鏈接了解:
如果這篇文章對(duì)你有所幫助,或者有所啟發(fā)的話,幫忙點(diǎn)贊、在看、轉(zhuǎn)發(fā)、收藏,你的支持就是我堅(jiān)持下去的最大動(dòng)力!