如何優(yōu)雅地玩轉(zhuǎn)分庫分表

在談?wù)摂?shù)據(jù)庫架構(gòu)和數(shù)據(jù)庫優(yōu)化的時(shí)候,我們經(jīng)常會(huì)聽到“分庫分表”、“分片”芝雪、“Sharding”…這樣的關(guān)鍵詞。讓人感到高興的是综苔,這些朋友所服務(wù)的公司業(yè)務(wù)量正在(或者即將面臨)高速增長惩系,技術(shù)方面也面臨著一些挑戰(zhàn)。讓人感到擔(dān)憂的是如筛,他們系統(tǒng)真的就需要“分庫分表”了嗎堡牡?“分庫分表”有那么容易實(shí)踐嗎?為此杨刨,筆者整理了分庫分表中可能遇到的一些問題晤柄,并結(jié)合以往經(jīng)驗(yàn)介紹了對(duì)應(yīng)的解決思路和建議。

垂直分表

垂直分表在日常開發(fā)和設(shè)計(jì)中比較常見妖胀,通俗的說法叫做“大表拆小表”芥颈,拆分是基于關(guān)系型數(shù)據(jù)庫中的“列”(字段)進(jìn)行的。通常情況赚抡,某個(gè)表中的字段比較多爬坑,可以新建立一張“擴(kuò)展表”,將不經(jīng)常使用或者長度較大的字段拆分出去放到“擴(kuò)展表”中怕品,如下圖所示:

小結(jié)

在字段很多的情況下妇垢,拆分開確實(shí)更便于開發(fā)和維護(hù)(筆者曾見過某個(gè)遺留系統(tǒng)中,一個(gè)大表中包含100多列的)肉康。某種意義上也能避免“跨頁”的問題(MySQL闯估、MSSQL底層都是通過“數(shù)據(jù)頁”來存儲(chǔ)的,“跨頁”問題可能會(huì)造成額外的性能開銷吼和,這里不展開涨薪,感興趣的朋友可以自行查閱相關(guān)資料進(jìn)行研究)。

拆分字段的操作建議在數(shù)據(jù)庫設(shè)計(jì)階段就做好炫乓。如果是在發(fā)展過程中拆分刚夺,則需要改寫以前的查詢語句,會(huì)額外帶來一定的成本和風(fēng)險(xiǎn)末捣,建議謹(jǐn)慎侠姑。

垂直分庫

垂直分庫在“微服務(wù)”盛行的今天已經(jīng)非常普及了÷嶙觯基本的思路就是按照業(yè)務(wù)模塊來劃分出不同的數(shù)據(jù)庫莽红,而不是像早期一樣將所有的數(shù)據(jù)表都放到同一個(gè)數(shù)據(jù)庫中。如下圖:

小結(jié)

系統(tǒng)層面的“服務(wù)化”拆分操作邦邦,能夠解決業(yè)務(wù)系統(tǒng)層面的耦合和性能瓶頸油吭,有利于系統(tǒng)的擴(kuò)展維護(hù)待榔。而數(shù)據(jù)庫層面的拆分,道理也是相通的。與服務(wù)的“治理”和“降級(jí)”機(jī)制類似痕惋,我們也能對(duì)不同業(yè)務(wù)類型的數(shù)據(jù)進(jìn)行“分級(jí)”管理、維護(hù)攘残、監(jiān)控讨惩、擴(kuò)展等。

眾所周知氏身,數(shù)據(jù)庫往往最容易成為應(yīng)用系統(tǒng)的瓶頸巍棱,而數(shù)據(jù)庫本身屬于“有狀態(tài)”的,相對(duì)于Web和應(yīng)用服務(wù)器來講观谦,是比較難實(shí)現(xiàn)“橫向擴(kuò)展”的拉盾。數(shù)據(jù)庫的連接資源比較寶貴且單機(jī)處理能力也有限,在高并發(fā)場(chǎng)景下豁状,垂直分庫一定程度上能夠突破IO捉偏、連接數(shù)及單機(jī)硬件資源的瓶頸,是大型分布式系統(tǒng)中優(yōu)化數(shù)據(jù)庫架構(gòu)的重要手段泻红。

然后夭禽,很多人并沒有從根本上搞清楚為什么要拆分,也沒有掌握拆分的原則和技巧谊路,只是一味的模仿大廠的做法讹躯。導(dǎo)致拆分后遇到很多問題(例如:跨庫join,分布式事務(wù)等)。

水平分表

水平分表也稱為橫向分表潮梯,比較容易理解骗灶,就是將表中不同的數(shù)據(jù)行按照一定規(guī)律分布到不同的數(shù)據(jù)庫表中(這些表保存在同一個(gè)數(shù)據(jù)庫中),這樣來降低單表數(shù)據(jù)量秉馏,優(yōu)化查詢性能耙旦。最常見的方式就是通過主鍵或者時(shí)間等字段進(jìn)行Hash和取模后拆分。如下圖所示:

小結(jié)

水平分表萝究,能夠降低單表的數(shù)據(jù)量免都,一定程度上可以緩解查詢性能瓶頸。但本質(zhì)上這些表還保存在同一個(gè)庫中帆竹,所以庫級(jí)別還是會(huì)有IO瓶頸绕娘。所以,一般不建議采用這種做法栽连。

水平分庫分表

水平分庫分表與上面講到的水平分表的思想相同险领,唯一不同的就是將這些拆分出來的表保存在不同的數(shù)據(jù)中。這也是很多大型互聯(lián)網(wǎng)公司所選擇的做法升酣。如下圖:

某種意義上來講舷暮,有些系統(tǒng)中使用的“冷熱數(shù)據(jù)分離”(將一些使用較少的歷史數(shù)據(jù)遷移到其他的數(shù)據(jù)庫中。而在業(yè)務(wù)功能上噩茄,通常默認(rèn)只提供熱點(diǎn)數(shù)據(jù)的查詢)下面,也是類似的實(shí)踐。在高并發(fā)和海量數(shù)據(jù)的場(chǎng)景下绩聘,分庫分表能夠有效緩解單機(jī)和單庫的性能瓶頸和壓力沥割,突破IO、連接數(shù)凿菩、硬件資源的瓶頸机杜。當(dāng)然,投入的硬件成本也會(huì)更高衅谷。同時(shí)椒拗,這也會(huì)帶來一些復(fù)雜的技術(shù)問題和挑戰(zhàn)(例如:跨分片的復(fù)雜查詢,跨分片事務(wù)等)

分庫分表的難點(diǎn)

垂直分庫帶來的問題和解決思路:

跨庫join的問題

在拆分之前获黔,系統(tǒng)中很多列表和詳情頁所需的數(shù)據(jù)是可以通過sql join來完成的蚀苛。而拆分后,數(shù)據(jù)庫可能是分布式在不同實(shí)例和不同的主機(jī)上玷氏,join將變得非常麻煩堵未。而且基于架構(gòu)規(guī)范,性能盏触,安全性等方面考慮渗蟹,一般是禁止跨庫join的块饺。那該怎么辦呢?首先要考慮下垂直分庫的設(shè)計(jì)問題雌芽,如果可以調(diào)整授艰,那就優(yōu)先調(diào)整。如果無法調(diào)整的情況膘怕,下面筆者將結(jié)合以往的實(shí)際經(jīng)驗(yàn)想诅,總結(jié)幾種常見的解決思路召庞,并分析其適用場(chǎng)景岛心。

跨庫Join的幾種解決思路

全局表

所謂全局表,就是有可能系統(tǒng)中所有模塊都可能會(huì)依賴到的一些表篮灼。比較類似我們理解的“數(shù)據(jù)字典”忘古。為了避免跨庫join查詢,我們可以將這類表在其他每個(gè)數(shù)據(jù)庫中均保存一份诅诱。同時(shí)髓堪,這類數(shù)據(jù)通常也很少發(fā)生修改(甚至幾乎不會(huì)),所以也不用太擔(dān)心“一致性”問題娘荡。

字段冗余

這是一種典型的反范式設(shè)計(jì)干旁,在互聯(lián)網(wǎng)行業(yè)中比較常見,通常是為了性能來避免join查詢炮沐。

舉個(gè)電商業(yè)務(wù)中很簡單的場(chǎng)景:

“訂單表”中保存“賣家Id”的同時(shí)争群,將賣家的“Name”字段也冗余,這樣查詢訂單詳情的時(shí)候就不需要再去查詢“賣家用戶表”大年。

字段冗余能帶來便利换薄,是一種“空間換時(shí)間”的體現(xiàn)。但其適用場(chǎng)景也比較有限翔试,比較適合依賴字段較少的情況轻要。最復(fù)雜的還是數(shù)據(jù)一致性問題,這點(diǎn)很難保證垦缅,可以借助數(shù)據(jù)庫中的觸發(fā)器或者在業(yè)務(wù)代碼層面去保證冲泥。當(dāng)然,也需要結(jié)合實(shí)際業(yè)務(wù)場(chǎng)景來看一致性的要求壁涎。就像上面例子凡恍,如果賣家修改了Name之后,是否需要在訂單信息中同步更新呢粹庞?

數(shù)據(jù)同步

定時(shí)A庫中的tab_a表和B庫中tbl_b有關(guān)聯(lián)咳焚,可以定時(shí)將指定的表做同步。當(dāng)然庞溜,同步本來會(huì)對(duì)數(shù)據(jù)庫帶來一定的影響革半,需要性能影響和數(shù)據(jù)時(shí)效性中取得一個(gè)平衡碑定。這樣來避免復(fù)雜的跨庫查詢。筆者曾經(jīng)在項(xiàng)目中是通過ETL工具來實(shí)施的又官。

系統(tǒng)層組裝

在系統(tǒng)層面延刘,通過調(diào)用不同模塊的組件或者服務(wù),獲取到數(shù)據(jù)并進(jìn)行字段拼裝六敬。說起來很容易碘赖,但實(shí)踐起來可真沒有這么簡單,尤其是數(shù)據(jù)庫設(shè)計(jì)上存在問題但又無法輕易調(diào)整的時(shí)候外构。

具體情況通常會(huì)比較復(fù)雜普泡。下面筆者結(jié)合以往實(shí)際經(jīng)驗(yàn),并通過偽代碼方式來描述审编。

簡單的列表查詢的情況

偽代碼很容易理解撼班,先獲取“我的提問列表”數(shù)據(jù),然后再根據(jù)列表中的UserId去循環(huán)調(diào)用依賴的用戶服務(wù)獲取到用戶的RealName垒酬,拼裝結(jié)果并返回砰嘁。

有經(jīng)驗(yàn)的讀者一眼就能看出上訴偽代碼存在效率問題。循環(huán)調(diào)用服務(wù)勘究,可能會(huì)有循環(huán)RPC矮湘,循環(huán)查詢數(shù)據(jù)庫…不推薦使用。再看看改進(jìn)后的:

這種實(shí)現(xiàn)方式口糕,看起來要優(yōu)雅一點(diǎn)缅阳,其實(shí)就是把循環(huán)調(diào)用改成一次調(diào)用。當(dāng)然走净,用戶服務(wù)的數(shù)據(jù)庫查詢中很可能是In查詢券时,效率方面比上一種方式更高。(坊間流傳In查詢會(huì)全表掃描伏伯,存在性能問題橘洞,傳聞不可全信。其實(shí)查詢優(yōu)化器都是基本成本估算的说搅,經(jīng)過測(cè)試炸枣,在In語句中條件字段有索引的時(shí)候,條件較少的情況是會(huì)走索引的弄唧。這里不細(xì)展開說明适肠,感興趣的朋友請(qǐng)自行測(cè)試)。

小結(jié)

簡單字段組裝的情況下候引,我們只需要先獲取“主表”數(shù)據(jù)侯养,然后再根據(jù)關(guān)聯(lián)關(guān)系,調(diào)用其他模塊的組件或服務(wù)來獲取依賴的其他字段(如例中依賴的用戶信息)澄干,最后將數(shù)據(jù)進(jìn)行組裝逛揩。

通常柠傍,我們都會(huì)通過緩存來避免頻繁RPC通信和數(shù)據(jù)庫查詢的開銷。

列表查詢帶條件過濾的情況

在上述例子中辩稽,都是簡單的字段組裝惧笛,而不存在條件過濾〕研梗看拆分前的SQL:

這種連接查詢并且還帶條件過濾的情況患整,想在代碼層面組裝數(shù)據(jù)其實(shí)是非常復(fù)雜的(尤其是左表和右表都帶條件過濾的情況會(huì)更復(fù)雜),不能像之前例子中那樣簡單的進(jìn)行組裝了喷众。試想一下各谚,如果像上面那樣簡單的進(jìn)行組裝,造成的結(jié)果就是返回的數(shù)據(jù)不完整侮腹,不準(zhǔn)確嘲碧。

有如下幾種解決思路:

查出所有的問答數(shù)據(jù),然后調(diào)用用戶服務(wù)進(jìn)行拼裝數(shù)據(jù)父阻,再根據(jù)過濾字段state字段進(jìn)行過濾,最后進(jìn)行排序和分頁并返回望抽。

這種方式能夠保證數(shù)據(jù)的準(zhǔn)確性和完整性加矛,但是性能影響非常大,不建議使用煤篙。

查詢出state字段符合/不符合的UserId斟览,在查詢問答數(shù)據(jù)的時(shí)候使用in/not in進(jìn)行過濾,排序辑奈,分頁等苛茂。過濾出有效的問答數(shù)據(jù)后,再調(diào)用用戶服務(wù)獲取數(shù)據(jù)進(jìn)行組裝鸠窗。

這種方式明顯更優(yōu)雅點(diǎn)妓羊。筆者之前在某個(gè)項(xiàng)目的特殊場(chǎng)景中就是采用過這種方式實(shí)現(xiàn)。

跨庫事務(wù)(分布式事務(wù))的問題

按業(yè)務(wù)拆分?jǐn)?shù)據(jù)庫之后稍计,不可避免的就是“分布式事務(wù)”的問題躁绸。以往在代碼中通過spring注解簡單配置就能實(shí)現(xiàn)事務(wù)的,現(xiàn)在則需要花很大的成本去保證一致性臣嚣。

垂直分庫總結(jié)和實(shí)踐建議

本篇中主要描述了幾種常見的拆分方式净刮,并著重介紹了垂直分庫帶來的一些問題和解決思路。讀者朋友可能還有些問題和疑惑硅则。

1. 我們目前的數(shù)據(jù)庫是否需要進(jìn)行垂直分庫淹父?

根據(jù)系統(tǒng)架構(gòu)和公司實(shí)際情況來,如果你們的系統(tǒng)還是個(gè)簡單的單體應(yīng)用怎虫,并且沒有什么訪問量和數(shù)據(jù)量暑认,那就別著急折騰“垂直分庫”了督暂,否則沒有任何收益,也很難有好結(jié)果穷吮。

切記逻翁,“過度設(shè)計(jì)”和“過早優(yōu)化”是很多架構(gòu)師和技術(shù)人員常犯的毛病。

2. 垂直拆分有沒有原則或者技巧捡鱼?

沒有什么黃金法則和標(biāo)準(zhǔn)答案八回。一般是參考系統(tǒng)的業(yè)務(wù)模塊拆分來進(jìn)行數(shù)據(jù)庫的拆分。比如“用戶服務(wù)”驾诈,對(duì)應(yīng)的可能就是“用戶數(shù)據(jù)庫”缠诅。但是也不一定嚴(yán)格一一對(duì)應(yīng)。有些情況下乍迄,數(shù)據(jù)庫拆分的粒度可能會(huì)比系統(tǒng)拆分的粒度更粗管引。筆者也確實(shí)見過有些系統(tǒng)中的某些表原本應(yīng)該放A庫中的,卻放在了B庫中闯两。有些庫和表原本是可以合并的褥伴,卻單獨(dú)保存著。還有些表漾狼,看起來放在A庫中也OK重慢,放在B庫中也合理。

如何設(shè)計(jì)和權(quán)衡逊躁,這個(gè)就看實(shí)際情況和架構(gòu)師/開發(fā)人員的水平了似踱。

3. 上面舉例的都太簡單了,我們的后臺(tái)報(bào)表系統(tǒng)中join的表都有n個(gè)了稽煤,

分庫后該怎么查核芽?

有很多朋友跟我提過類似的問題。其實(shí)互聯(lián)網(wǎng)的業(yè)務(wù)系統(tǒng)中酵熙,本來就應(yīng)該盡量避免join的轧简,如果有多個(gè)join的,要么是設(shè)計(jì)不合理绿店,要么是技術(shù)選型有誤吉懊。請(qǐng)自行科普下OLAP和OLTP,報(bào)表類的系統(tǒng)在傳統(tǒng)BI時(shí)代都是通過OLAP數(shù)據(jù)倉庫去實(shí)現(xiàn)的(現(xiàn)在則更多是借助離線分析假勿、流式計(jì)算等手段實(shí)現(xiàn))借嗽,而不該向上面描述的那樣直接在業(yè)務(wù)庫中執(zhí)行大量join和統(tǒng)計(jì)。

如果想學(xué)習(xí)Java工程化转培、高性能及分布式恶导、深入淺出。微服務(wù)浸须、Spring惨寿,MyBatis邦泄,Netty源碼分析的朋友可以加我的Java高級(jí)交流:787707172,群里有阿里大牛直播講解技術(shù)裂垦,以及Java大型互聯(lián)網(wǎng)技術(shù)的視頻免費(fèi)分享給大家顺囊。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市蕉拢,隨后出現(xiàn)的幾起案子特碳,更是在濱河造成了極大的恐慌,老刑警劉巖晕换,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件午乓,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡闸准,警方通過查閱死者的電腦和手機(jī)益愈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來夷家,“玉大人蒸其,你說我怎么就攤上這事●ⅲ” “怎么了枣接?”我有些...
    開封第一講書人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長缺谴。 經(jīng)常有香客問我,道長耳鸯,這世上最難降的妖魔是什么湿蛔? 我笑而不...
    開封第一講書人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮县爬,結(jié)果婚禮上阳啥,老公的妹妹穿的比我還像新娘。我一直安慰自己财喳,他們只是感情好察迟,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著耳高,像睡著了一般扎瓶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上泌枪,一...
    開封第一講書人閱讀 49,764評(píng)論 1 290
  • 那天概荷,我揣著相機(jī)與錄音,去河邊找鬼碌燕。 笑死误证,一個(gè)胖子當(dāng)著我的面吹牛继薛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播愈捅,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼遏考,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了蓝谨?” 一聲冷哼從身側(cè)響起灌具,我...
    開封第一講書人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎像棘,沒想到半個(gè)月后稽亏,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡缕题,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年截歉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片烟零。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡瘪松,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出锨阿,到底是詐尸還是另有隱情宵睦,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布墅诡,位于F島的核電站壳嚎,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏末早。R本人自食惡果不足惜烟馅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望然磷。 院中可真熱鬧郑趁,春花似錦、人聲如沸姿搜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽舅柜。三九已至梭纹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間业踢,已是汗流浹背栗柒。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人瞬沦。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓太伊,卻偏偏與公主長得像,于是被迫代替她去往敵國和親逛钻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子僚焦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容