關(guān)于分庫(kù)分表的一些事兒

背景

前幾天在流動(dòng)工位聽(tīng)到一同事在電話面試候選人,聽(tīng)他問(wèn):“分庫(kù)分表接觸過(guò)嗎,分別是為了解決什么問(wèn)題”,讓我聯(lián)想到曾經(jīng)有一個(gè)面試官問(wèn)沒(méi)接觸過(guò)分庫(kù)分表的我——如果由我來(lái)設(shè)計(jì),如何實(shí)現(xiàn)分庫(kù)分表?

是啊,分庫(kù)分表原理是什么,具體又應(yīng)該是怎么實(shí)現(xiàn)的,我當(dāng)時(shí)的想法對(duì)不對(duì)呢?

介紹

單庫(kù)表的問(wèn)題

通常在業(yè)務(wù)發(fā)展初期,一系列相關(guān)數(shù)據(jù)通常存儲(chǔ)在一張單表中,比如user表,article表等。隨著業(yè)務(wù)的發(fā)展,單表存儲(chǔ)的數(shù)據(jù)量可能十分巨大,達(dá)到幾千萬(wàn)甚至上億;同時(shí)一張表的字段可能也擴(kuò)展到幾十上百個(gè),此時(shí)可能帶來(lái)一系列問(wèn)題:

  • 資源不足
    • 單機(jī)的IO、連接數(shù)都是有限的,一旦并發(fā)量過(guò)高DB就會(huì)成為整個(gè)系統(tǒng)的瓶頸
  • 讀寫(xiě)效率降低
    • 寫(xiě)操作,鎖沖突現(xiàn)象一定會(huì)更明顯
    • 讀操作,由于數(shù)據(jù)量過(guò)大及單行數(shù)據(jù)過(guò)多,某些批量查詢一定伴隨著頻繁IO,也難以命中緩存
  • 冷熱數(shù)據(jù)混雜
    • 一部分字段實(shí)際上并不需要經(jīng)常讀寫(xiě),導(dǎo)致單行數(shù)據(jù)也存在冷熱之分
  • 索引膨脹
    • 數(shù)據(jù)量的增加伴隨著索引量的增加,對(duì)索引的操作耗時(shí)也連帶著提高

分庫(kù)分表前的鋪墊工作

切勿過(guò)渡設(shè)計(jì),切勿過(guò)早優(yōu)化。

千萬(wàn)不要為了追求引入新技術(shù)而直接分庫(kù)分表,這會(huì)導(dǎo)致系統(tǒng)復(fù)雜性呈指數(shù)上升。在進(jìn)行分庫(kù)分表前,至少應(yīng)該做好如下鋪墊工作:

  • SQL優(yōu)化 減少慢查詢,騰出連接資源
  • 索引優(yōu)化 減少非索引查詢,新建索引的成本遠(yuǎn)低于架構(gòu)改變
  • 增加緩存 減少DB壓力,改造成本低
  • 主從分離 寫(xiě)操作以及事務(wù)中的讀操作走主庫(kù),其他讀操作走從庫(kù),需要注意主備延遲

對(duì)于大多數(shù)業(yè)務(wù)系統(tǒng)來(lái)說(shuō)讀遠(yuǎn)多于寫(xiě),增加緩存、主從分離后問(wèn)題應(yīng)該能有很大緩解。

但是即便做了上述工作,還是沒(méi)有解決根本性的單表數(shù)據(jù)量過(guò)大和字段過(guò)多的問(wèn)題,在業(yè)務(wù)繼續(xù)發(fā)展過(guò)程中問(wèn)題一定還會(huì)再次出現(xiàn)。此時(shí)除了分庫(kù)分表或修改業(yè)務(wù)邏輯使用NoSql數(shù)據(jù)庫(kù)外,幾乎沒(méi)有其他太好的方法。

水平/垂直分庫(kù)/分表是什么

  • 分庫(kù) 即將原本存儲(chǔ)在單庫(kù)的表分散存儲(chǔ)在多個(gè)庫(kù)中
  • 分表 將一張大表分散存儲(chǔ)在多個(gè)表中

垂直分表

根據(jù)關(guān)聯(lián)度或業(yè)務(wù)屬性按列將表拆分,如頻繁展示的“核心信息”與對(duì)應(yīng)的“詳情信息”拆分。

拆分后的表之間結(jié)構(gòu)不相同,數(shù)據(jù)存在相互關(guān)聯(lián),有小部分重疊,如user_id對(duì)應(yīng)。

垂直分表

水平分表

根據(jù)一定的策略將表中的數(shù)據(jù)行分別存儲(chǔ)在不同的表中,如按創(chuàng)建時(shí)間/id哈希等拆分。

拆分后的表結(jié)構(gòu)相同,但存儲(chǔ)的數(shù)據(jù)不重疊。

水平分表

垂直分庫(kù)

將業(yè)務(wù)關(guān)聯(lián)度不高的表分離到不同庫(kù)中,如拆分為“用戶庫(kù)”、“商品庫(kù)”。

垂直分庫(kù)

水平分庫(kù)

將水平分表后的多張表再次分散到不同庫(kù)中存儲(chǔ),減少單庫(kù)壓力。

水平分庫(kù)

分庫(kù)分表解決的問(wèn)題

分庫(kù)的核心目的:

  • 將原本單庫(kù)的機(jī)器資源擴(kuò)充為多庫(kù),包括IO、連接數(shù)等
  • 冷熱數(shù)據(jù)分離,將更多資源分配給頻繁訪問(wèn)的數(shù)據(jù)

分表的核心目的:

  • 減少單表列數(shù),減小數(shù)據(jù)塊大小,減少I(mǎi)O次數(shù)及提高緩存利用率,減少單表IO壓力(垂直分表)
  • 冷熱數(shù)據(jù)分離,可以將數(shù)據(jù)分為不常變更的數(shù)據(jù)和常變更的數(shù)據(jù),分配不同的訪問(wèn)策略(垂直分表)
  • 減少單表行數(shù),將數(shù)據(jù)分散存儲(chǔ)到多張表中(水平分表)
  • 冷熱數(shù)據(jù)分離,在一些時(shí)效性強(qiáng)的場(chǎng)景下將當(dāng)前數(shù)據(jù)和過(guò)時(shí)數(shù)據(jù)分開(kāi)處理(水平分表)
  • 減少單表索引量,提升讀寫(xiě)效率(水平分表)
  • 一些分庫(kù)操作的前提條件,即先做好分表設(shè)計(jì)后可以將表分布在不同庫(kù)中存儲(chǔ)

分庫(kù)分表我的思路

  1. 建立中間層,對(duì)應(yīng)用層和數(shù)據(jù)庫(kù)做到無(wú)侵入式對(duì)接,所有的配置和路由等問(wèn)題全部在中間層處理
  2. 設(shè)計(jì)路由規(guī)則,配置好需要的各個(gè)庫(kù)
  3. 接收并解析應(yīng)用層SQL語(yǔ)句,提取表名、字段名等關(guān)鍵信息
  4. 根據(jù)路由規(guī)則計(jì)算目標(biāo)庫(kù)表名,如果查詢目標(biāo)分布在多個(gè)庫(kù)表還需要將單條查詢拆成多條查詢
  5. 生成新的SQL語(yǔ)句或替換原有語(yǔ)句
  6. 在對(duì)應(yīng)庫(kù)表中進(jìn)行查詢
  7. 將返回結(jié)果根據(jù)查詢語(yǔ)句類型進(jìn)行融合,如COUNT、SUM、ORDER BY等
  8. 返回查詢結(jié)果

當(dāng)然在實(shí)際應(yīng)用中還需要考慮更多系統(tǒng)層面的保障,包括降級(jí)熔斷、讀寫(xiě)分離、宕機(jī)、擴(kuò)容等等。

分庫(kù)分表如何實(shí)現(xiàn)

常見(jiàn)的分庫(kù)分表中間件有:

  • ShardingSphere 前身為Sharding-JDBC,目前已成為Apache基金會(huì)頂級(jí)項(xiàng)目
  • TDDL 阿里開(kāi)源(開(kāi)源了嗎?)
  • MyCat 民間開(kāi)源

分庫(kù)分表中間件主要是處理水平分庫(kù)分表的問(wèn)題,而垂直分庫(kù)分表本身處理邏輯比較簡(jiǎn)單,在應(yīng)用層也容易實(shí)現(xiàn)和維護(hù)。

而分庫(kù)分表最重要的能力則是SQL的解析、路由與轉(zhuǎn)發(fā),以及相關(guān)的支撐能力包括配置、擴(kuò)容等。

以TDDL為例,處理流程為:

來(lái)自參考資料

一般而言的業(yè)務(wù)演化改造流程:

  1. 單庫(kù)單表
    • 重要業(yè)務(wù)數(shù)據(jù)可考慮直接主從分離,此時(shí)考慮的更多是數(shù)據(jù)容災(zāi)問(wèn)題
    • 數(shù)據(jù)量千萬(wàn)以下完全撐得住
  2. SQL優(yōu)化
    • 避免慢查詢占用連接資源
  3. 索引優(yōu)化
    • 優(yōu)化索引相對(duì)成本低,投入產(chǎn)出比高
  4. 增加緩存/讀寫(xiě)分離
    • 針對(duì)大多數(shù)讀多寫(xiě)少的業(yè)務(wù)幫助較大
  5. 垂直分庫(kù)
    • 增加機(jī)器資源,降低查詢壓力
    • 開(kāi)始深入優(yōu)化,改造難度上升,維護(hù)也變得復(fù)雜,需要注意的包括但不限于:
      • 數(shù)據(jù)遷移
      • 分布式事務(wù)
      • 全劇唯一id
      • 冷熱數(shù)據(jù)分離,可以單獨(dú)給熱區(qū)數(shù)據(jù)做分庫(kù)分表
      • 服務(wù)器資源規(guī)劃
  6. 垂直分表
    • 改造成本更高,應(yīng)用層邏輯可能需要改造,如數(shù)據(jù)雙寫(xiě)等
  7. 水平分表
    • 當(dāng)單表數(shù)據(jù)量過(guò)大時(shí),需要借助中間件進(jìn)行分表
  8. 水平分庫(kù)
    • 擴(kuò)容,增加機(jī)器資源
  9. 如果還是撐不住,試試其他存儲(chǔ)方式吧,比如NoSql數(shù)據(jù)庫(kù)

分庫(kù)分表帶來(lái)的問(wèn)題

自增主鍵

分表后各表無(wú)法同步自增id,需要依賴全局唯一id生成方案實(shí)現(xiàn),如snowflake等。

數(shù)據(jù)更新

垂直分庫(kù)分表后,一些單表更新變成多表更新。

數(shù)據(jù)庫(kù)事務(wù)

由于跨庫(kù)的問(wèn)題,原本的數(shù)據(jù)庫(kù)事務(wù)難以支持,需要借助分布式事務(wù)實(shí)現(xiàn)。

關(guān)聯(lián)操作

由于跨庫(kù),join等操作無(wú)法實(shí)現(xiàn),關(guān)聯(lián)查詢很難實(shí)現(xiàn),大多數(shù)應(yīng)用通過(guò)冗余存儲(chǔ)或借助其他存儲(chǔ)方式實(shí)現(xiàn)。

外鍵約束/全文索引

分庫(kù)分表后無(wú)法支持全文索引或外鍵約束,正如一些參考資料所說(shuō),現(xiàn)今的業(yè)務(wù)本身最好就盡最大可能避免這樣的操作發(fā)生,以本人從業(yè)經(jīng)驗(yàn)來(lái)看也從未遇到應(yīng)用場(chǎng)景。

性能開(kāi)銷

原本的一些單表查詢?cè)诜謳?kù)分表后可能變?yōu)槎啾聿樵儯约邦~外的數(shù)據(jù)拼接處理開(kāi)銷。但相比單表海量數(shù)據(jù)的讀寫(xiě)壓力而言這些額外開(kāi)銷是可以接受的。

運(yùn)維管理

從單庫(kù)單表到分庫(kù)分表,維護(hù)難度呈指數(shù)增長(zhǎng),尤其是涉及到擴(kuò)容、宕機(jī)等等。

總結(jié)

分庫(kù)分表中間件本質(zhì)上就是將應(yīng)用層的多表查詢和結(jié)果處理能力分離,進(jìn)行抽象化和標(biāo)準(zhǔn)化。

雖然核心思路并不復(fù)雜,但整個(gè)服務(wù)的體系化,包括維護(hù)、容災(zāi)等才是最困難的部分。同時(shí),當(dāng)面試官問(wèn)過(guò)分庫(kù)分表的問(wèn)題后,緊接著可以展開(kāi)的內(nèi)容也非常豐富,包括全局唯一id、數(shù)據(jù)庫(kù)鎖、分布式事務(wù)、主從同步、負(fù)載均衡、一致性哈希、限流、緩存......能夠關(guān)聯(lián)到很多的知識(shí),是一個(gè)非常好的切入點(diǎn)。

參考資料

概覽 :: ShardingSphere

分布式數(shù)據(jù)庫(kù)中間件—TDDL的使用介紹 - 知乎

不要NoSQL/NewSQL,也不要分區(qū),直接分庫(kù)分表!_存儲(chǔ)

Mycat1.6

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容