自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

關(guān)于分庫(kù)分表,你知道多少?

數(shù)據(jù)庫(kù)
本質(zhì)上分庫(kù)分表是三個(gè)概念,這里我們都是從水平拓展的維度討論問(wèn)題,本質(zhì)上分庫(kù)分表中的概念分別對(duì)應(yīng):分庫(kù)、分表和分庫(kù)分表。

在業(yè)務(wù)體量還不是很大的時(shí)候,單庫(kù)單表即可滿足業(yè)務(wù)上的需求,隨著業(yè)務(wù)體量的增大,無(wú)論是CPU還是IO都可能出現(xiàn)性能瓶頸,由于大量連接達(dá)到單庫(kù)上,導(dǎo)致單庫(kù)無(wú)法承載這些活躍的連接數(shù),這使得我們從Java進(jìn)程的角度看來(lái)就是數(shù)據(jù)庫(kù)連接很少或者沒(méi)有連接可用,最終出現(xiàn)并發(fā)、吞吐全面下降甚至是系統(tǒng)崩潰。

所以,筆者整理了這篇分庫(kù)分表的文章來(lái)逐一分析拆解這些問(wèn)題。

一、關(guān)于一些分庫(kù)分表性能指標(biāo)的補(bǔ)充

這里我們補(bǔ)充一下IO瓶頸和CPU瓶頸,關(guān)于IO瓶頸,即是數(shù)據(jù)表中存在大量熱點(diǎn)數(shù)據(jù),大量的請(qǐng)求都需要到數(shù)據(jù)庫(kù)進(jìn)行查詢,因?yàn)榇罅康腎O請(qǐng)求進(jìn)來(lái)導(dǎo)致數(shù)據(jù)庫(kù)連接數(shù)不足導(dǎo)致性能瓶頸這就是所謂的IO瓶頸,針對(duì)這種情況我們可以考慮根據(jù)熱點(diǎn)數(shù)據(jù)類型采取垂直分表或者分庫(kù)的方式解決。

就像下面這種情況,因?yàn)橛写罅空?qǐng)求專門查詢用戶名和地址,所以我們采用垂直分表的方式將熱點(diǎn)數(shù)據(jù)拆出來(lái)獨(dú)立維護(hù),解決原有訂單表過(guò)度冗余的字段使得熱點(diǎn)數(shù)據(jù)體量直接減小,再通過(guò)內(nèi)存中間件加以緩存緩解數(shù)據(jù)庫(kù)壓力解決IO性能瓶頸:

假如熱點(diǎn)數(shù)據(jù)在垂直分表后,數(shù)據(jù)量可以減小,那么我們就采取垂直分表結(jié)合緩存中間件的方式解決。如果熱點(diǎn)數(shù)據(jù)無(wú)法通過(guò)垂直分表或者說(shuō)通過(guò)垂直分表后數(shù)據(jù)規(guī)模仍然很大的話,那么我們就必須通過(guò)水平分庫(kù)解決了。

而CPU瓶頸則是因?yàn)楸黻P(guān)聯(lián)join或者各種運(yùn)算例如group by,order by等導(dǎo)致查詢效率低下,這種情況如果無(wú)法通過(guò)索引或者業(yè)務(wù)代碼層面進(jìn)行計(jì)算的方式解決的話,那么就只能通過(guò)業(yè)務(wù)層面并結(jié)合水平分表縮小數(shù)據(jù)體量提升數(shù)據(jù)聚合效率。

二、分庫(kù)分表基本概念介紹

1. 為什么不使用MySQL分區(qū)

可能也會(huì)有讀者問(wèn)到,為什么不采用MySQL分區(qū)表呢? 這里我們需要了解一下MySQL分區(qū)表的工作原理,它會(huì)將分區(qū)的數(shù)據(jù)表在在物理層面進(jìn)行分區(qū),但在邏輯上還是一張表,這使得用戶在查詢的時(shí)候?qū)Ψ謪^(qū)是沒(méi)有感知的。所以說(shuō)使用MySQL分區(qū)會(huì)帶來(lái)以下好處:

  • 在一定的數(shù)據(jù)量情況下,使用分區(qū)鍵進(jìn)行查詢可以快速定位數(shù)據(jù)。
  • 因?yàn)榉謪^(qū)會(huì)在物理層面進(jìn)行切分,所以對(duì)于需要定期刪除分區(qū)數(shù)據(jù)的場(chǎng)景下,MySQL分區(qū)是非常方便管理的。

而同樣的它也存在如下缺點(diǎn):

  • 無(wú)法創(chuàng)建外鍵,當(dāng)然這對(duì)于現(xiàn)代開(kāi)發(fā)規(guī)范來(lái)說(shuō)這一點(diǎn)沒(méi)有太大影響。
  • 并發(fā)量上來(lái)了依然存在IO瓶頸。
  • 查詢時(shí)必須帶上分區(qū)鍵,否則會(huì)對(duì)所有分區(qū)進(jìn)行掃描。
  • 對(duì)分區(qū)查詢時(shí)的優(yōu)化都是由MySQL優(yōu)化器自定義,對(duì)用戶來(lái)說(shuō)是黑盒可控性較差,不如分庫(kù)分表靈活。

2. 分庫(kù)分表的基本概念

本質(zhì)上分庫(kù)分表是3個(gè)概念,這里我們都是從水平拓展的維度討論問(wèn)題,本質(zhì)上分庫(kù)分表中的概念分別對(duì)應(yīng):

(1) 分庫(kù):以8C16G的MySQL實(shí)例為例,經(jīng)過(guò)業(yè)界壓測(cè)普遍認(rèn)為其TPS大約在2500~3000左右,所以當(dāng)系統(tǒng)的并發(fā)量超過(guò)這個(gè)時(shí)候,就很可能出現(xiàn)連接數(shù)不足導(dǎo)致服務(wù)癱瘓的問(wèn)題,所以業(yè)界就有了水平分庫(kù)增加更多的數(shù)據(jù)庫(kù)連接的同時(shí)還能分散系統(tǒng)請(qǐng)求從而提升系統(tǒng)并發(fā)度:

(2) 分表:當(dāng)單表體量超過(guò)一定閾值之后,無(wú)論數(shù)據(jù)檢索還是修改操作對(duì)應(yīng)的耗時(shí)的都會(huì)增加,最經(jīng)典的就是深分頁(yè)問(wèn)題,此時(shí)我們就可以通過(guò)水平分表來(lái)縮小單表數(shù)據(jù)體量以提升數(shù)據(jù)檢索速度。

(3) 分庫(kù)分表:如果系統(tǒng)是典型高并發(fā)、海量數(shù)據(jù)的場(chǎng)景,也就是上述兩種情況的綜合體,那么我們就需要通過(guò)分庫(kù)分表這種綜合方案來(lái)解決問(wèn)題了。

三、分庫(kù)分表的兩種維度

1. 垂直分庫(kù)或者水平分庫(kù)

垂直分庫(kù)是解耦服務(wù)間依賴的常見(jiàn)手段,在傳統(tǒng)單體架構(gòu)時(shí),我們的所有的數(shù)據(jù)表都在一個(gè)數(shù)據(jù)庫(kù)中。隨著業(yè)務(wù)體量的增加,為了針對(duì)業(yè)務(wù)進(jìn)行優(yōu)化,我們可以將不同業(yè)務(wù)進(jìn)行圈表拆分到不同庫(kù)中,這就是垂直分庫(kù),通過(guò)垂直分庫(kù)進(jìn)行針對(duì)性優(yōu)化,從而針對(duì)這些業(yè)務(wù)孵化出一個(gè)業(yè)務(wù)模式,達(dá)到服務(wù)化。

因?yàn)楦卟l(fā)導(dǎo)致單點(diǎn)數(shù)據(jù)庫(kù)無(wú)法承載這些連接,所以我們將相同結(jié)構(gòu)的數(shù)據(jù)表放到不同的數(shù)據(jù)庫(kù),然后用戶通過(guò)分庫(kù)算法定位到這些數(shù)據(jù)進(jìn)行操作,以減輕數(shù)據(jù)庫(kù)的io和cpu壓力,這就是典型的水平分庫(kù)。

2. 垂直分表和水平分表

關(guān)于常見(jiàn)的分表技術(shù)有垂直分表和水平分表,其中垂直分表主要是優(yōu)化查詢的一種常見(jiàn)手段,從物理角度來(lái)說(shuō),它就是將一張表垂直進(jìn)行拆分以實(shí)現(xiàn)確保將熱點(diǎn)數(shù)據(jù)與非熱點(diǎn)數(shù)據(jù)進(jìn)行隔離,確保每次進(jìn)行查詢時(shí)緩存行可以盡可能緩存更多的字段,避免到磁盤進(jìn)行隨機(jī)IO導(dǎo)致的IO瓶頸。

水平分表則是為了解決大數(shù)據(jù)存儲(chǔ)和查詢問(wèn)題,從物理角度來(lái)說(shuō)它就是將大數(shù)據(jù)表橫切一刀分為無(wú)數(shù)張小表,然后所有的操作都需要針對(duì)體積更小的小表進(jìn)行操作,從而減小單體查詢檢索的IO量,提升檢索效率:

四、常見(jiàn)的分庫(kù)分表方案

1. 簡(jiǎn)介常見(jiàn)的集中分庫(kù)分表設(shè)計(jì)方案

為保證分庫(kù)分表后數(shù)據(jù)能夠被準(zhǔn)確的定位并查詢到,分表的策略也是很重要的,這里筆者列出幾種比較常見(jiàn)的分表方案:

  • range范圍發(fā)表法
  • hash取模法
  • range+hash法

2. range法分表

rang法實(shí)現(xiàn)比較簡(jiǎn)單,就是針對(duì)每個(gè)表都指定一個(gè)id范圍,假設(shè)我們現(xiàn)在有3張分表,分表1存儲(chǔ)1-500w的數(shù)據(jù),分表2存儲(chǔ)500w-1000w的數(shù)據(jù),分表3存儲(chǔ)1000w-1500w的數(shù)據(jù)。因?yàn)槊總€(gè)表范圍是固定的,那么我們?cè)谶M(jìn)行數(shù)據(jù)查詢時(shí)就很方便了,例如我們想查詢id為1500的訂單詳情,直接通過(guò)id%500w即定位到分表0。

這種方案在數(shù)據(jù)查詢比較均衡的情況下表現(xiàn)良好,遇到熱點(diǎn)問(wèn)題就比較棘手了,例如雙十一淘寶訂單都集中在分表3和分表4,這就會(huì)導(dǎo)致這兩張表單位時(shí)間內(nèi)承載大量查詢和操作請(qǐng)求,而其他表卻無(wú)法去負(fù)擔(dān)這些壓力,這也就是我們常說(shuō)的數(shù)據(jù)偏斜問(wèn)題。

3. hash取模法

于是就有一種均攤數(shù)據(jù)的分表算法即hash取模法,這種算法要求我們盡可能在功能實(shí)現(xiàn)前,評(píng)估將來(lái)的數(shù)據(jù)量,例如就是5000w,那么我們就設(shè)置10張表,每張表500w。后續(xù)進(jìn)行插入操作時(shí)我們只需根據(jù)自增id值進(jìn)行取模運(yùn)算然后均攤存儲(chǔ)到不同表即可。例如:我們現(xiàn)在有一條數(shù)據(jù)得到id為1000,通過(guò)1000%10=0,由此可知這條數(shù)據(jù)就可以存到tb_0表中.

hash分表算法雖然可以均攤數(shù)據(jù)存儲(chǔ),避免數(shù)據(jù)熱點(diǎn)問(wèn)題,但是也存在一定的缺點(diǎn),即查詢問(wèn)題,假如我們現(xiàn)在只有3張分表,id算法為id%3,一旦數(shù)據(jù)體量增加,我們的分表需要增加到6張,那么規(guī)則就需要改變了,很明顯這種改動(dòng)量存在的風(fēng)險(xiǎn)是非常大的。

4. range+hash法

由上可知range法可以很好的進(jìn)行擴(kuò)容,而hash法可以完美的均攤存儲(chǔ)。所以我們更建議使用range+hash法進(jìn)行分庫(kù)分表,通過(guò)range法決定當(dāng)前存儲(chǔ)的區(qū)域,再結(jié)合hash取模法指定這個(gè)區(qū)域中具體的一張表。

例如: 舉個(gè)例子,筆者現(xiàn)在根據(jù)業(yè)務(wù)需求對(duì)數(shù)據(jù)表進(jìn)行拆分得到6張分表:

  • 每張表存儲(chǔ)1000w條數(shù)據(jù)。
  • 1個(gè)庫(kù)作為一個(gè)range范圍,id自增。
  • 1個(gè)range包含兩張張分表,總和2000w數(shù)據(jù)。

對(duì)應(yīng)規(guī)則得到的表名和含義如下:

-- tb_0前綴 存儲(chǔ)0-2000w的數(shù)據(jù)
tb_0_0
tb_0_1

-- tb_1前綴 存儲(chǔ)2000-4000w的數(shù)據(jù)
tb_1_0
tb_1_1

-- tb_2前綴 存儲(chǔ)4000w-6000w的數(shù)據(jù)
tb_2_0
tb_2_1

根據(jù)我們上文所說(shuō),通過(guò)range決定區(qū)域,假設(shè)我們現(xiàn)在數(shù)據(jù)id為600w,根據(jù)上表前綴可知我們要存儲(chǔ)的數(shù)據(jù)表為tb_0開(kāi)頭的表,因?yàn)閠b_0開(kāi)頭的表有兩張,由此我們?cè)儆胔ash法進(jìn)行取模,即600w%2=0,由此可知數(shù)據(jù)最終要存到tb_0_0表。 我們?cè)倩剡^(guò)頭說(shuō)說(shuō)擴(kuò)容問(wèn)題,因?yàn)槲覀兺ㄟ^(guò)range法決定存儲(chǔ)的分表area,所以假設(shè)需要增加分表,我們也只需定義一個(gè)新的range范圍和這個(gè)范圍的分表算法即可。

就比如,我們現(xiàn)在就需要增加兩張分表,那么我們可以直接指定這兩張分表區(qū)域?yàn)?,的id范圍是6000w到8000w,因?yàn)檫@個(gè)區(qū)域還是兩張分表,所以算法也是hash%2,簡(jiǎn)單配置一下即可實(shí)現(xiàn)擴(kuò)容,無(wú)需對(duì)代碼進(jìn)行改造,可以說(shuō)這套方案相較于前兩者會(huì)更出色一些。

五、分庫(kù)分表涉及的一些涉及問(wèn)題

1. 分庫(kù)分表時(shí)如何選擇分表字段(推薦id)

整體來(lái)說(shuō)分庫(kù)分表的選用的字段可以有很多種的方案例如:

  • 按照用戶id
  • 按照時(shí)間
  • 按照地區(qū)

只不過(guò)在選擇的時(shí)候一定要結(jié)合業(yè)務(wù)場(chǎng)景進(jìn)行設(shè)計(jì)同時(shí)也要考慮到下面這兩個(gè)問(wèn)題:

  • 如何保證數(shù)據(jù)盡可能均勻分布到庫(kù)表,同時(shí)保證保證檢索效率?
  • 確定字段后,如何在數(shù)據(jù)檢索前明確知曉數(shù)據(jù)存在的庫(kù)表?

假設(shè)我們的查詢都是按照時(shí)間維度進(jìn)行查詢,那么我們就使用數(shù)據(jù)表中帶有時(shí)間性質(zhì)的字段作為分表字段,例如我們現(xiàn)在的訂單表order分表算法是按月進(jìn)行分表,在1月創(chuàng)建的數(shù)據(jù)存放至tb_1,在2月創(chuàng)建的數(shù)據(jù)存放至tb_2,那么我們雪花id算法作為分布式id生成工具,其原因如下:

  • 雪花算法自增有序,不會(huì)導(dǎo)致大量頁(yè)分裂而導(dǎo)致檢索性能下降問(wèn)題。
  • 雪花算法有41bit的空間記錄當(dāng)前時(shí)間戳,所以按照我們的分表算法,可以直接通過(guò)生成的id定位到日期從而確定庫(kù)表。

如下所示,我們1.25創(chuàng)建的數(shù)據(jù)得到的訂單id是1882967322877497344,基于高41bit得到時(shí)間是1月份所以存入分表1,后續(xù)查詢時(shí),我們只需要知道對(duì)應(yīng)訂單的id即可定位到分表從而利用主鍵索引檢索到數(shù)據(jù):

2. 分庫(kù)分表全局ID生成方案

分庫(kù)分表勢(shì)必涉及一些關(guān)于全局庫(kù)表id的設(shè)計(jì)方案,感興趣的讀者可以參考讀者這篇文章:《來(lái)聊聊大廠常用的分布式 ID 生成方案

3. 數(shù)據(jù)偏斜問(wèn)題和解決方案

數(shù)據(jù)偏斜即按照現(xiàn)有分庫(kù)分表算法出現(xiàn)了某份庫(kù)表數(shù)據(jù)遠(yuǎn)遠(yuǎn)大于其他表數(shù)據(jù),進(jìn)而導(dǎo)致:

  • 性能瓶頸:因?yàn)閿?shù)據(jù)偏斜導(dǎo)致數(shù)據(jù)分布不均勻,對(duì)于分表后的性能表現(xiàn)和分表前并沒(méi)有很大的提升
  • 資源利用不均勻
  • 查詢效率低下

數(shù)據(jù)偏斜問(wèn)題的根因大部分是分表算法設(shè)計(jì)不好所導(dǎo)致,例如上面提到的range分表法無(wú)法針對(duì)業(yè)務(wù)高峰期的id段進(jìn)行數(shù)據(jù)均攤,針對(duì)該問(wèn)題我們建議從以下幾個(gè)角度考慮并選用合適的方案:

  • 如果沒(méi)有特定的范圍查詢或者分頁(yè)查詢等需要,僅僅針對(duì)特定幾條數(shù)據(jù)的檢索,我們可以將分表算法改為hash算法結(jié)合取模運(yùn)算均勻分布數(shù)據(jù)。
  • 如果需要進(jìn)行特定日期等范圍查詢的要求,建議在特殊月份做特殊的分表算法,并針對(duì)該月份的分表規(guī)則進(jìn)行特殊處理,例如11月份訂單是平時(shí)的3倍,我們就可以在該月份多部署幾個(gè)庫(kù)源和服務(wù),針對(duì)該每個(gè)服務(wù)都有各自的workerId對(duì)應(yīng)一個(gè)庫(kù)源,如下圖一個(gè)java-service對(duì)應(yīng)一個(gè)tb_11_x的庫(kù),通過(guò)負(fù)載均衡算法將訂單請(qǐng)求打到不同的服務(wù)上以保證數(shù)據(jù)均勻的落到不同的庫(kù)表中。

4. 如何解決分庫(kù)分表后的join問(wèn)題

水平拓展后可能會(huì)導(dǎo)致庫(kù)表發(fā)布到不同的MySQL實(shí)例上,這使得原有的單數(shù)據(jù)源關(guān)聯(lián)查詢變?yōu)槎鄮?kù)源關(guān)聯(lián):

實(shí)際上解決該問(wèn)題的辦法有如下幾種:

  • 應(yīng)用層進(jìn)行關(guān)聯(lián),即應(yīng)用層面分別查詢兩張表然后將數(shù)據(jù)關(guān)聯(lián)。
  • 通過(guò)數(shù)據(jù)庫(kù)中間件例如shardingsphere等工具實(shí)現(xiàn),不過(guò)shardingsphere的聯(lián)邦查詢還不是很穩(wěn)定,慎用。
  • 將需要關(guān)聯(lián)查詢的數(shù)據(jù)直接冗余到分表上。
  • 通過(guò)es等搜索引擎統(tǒng)一結(jié)構(gòu)化存儲(chǔ)提供外部檢索查詢。

5. 非partition key查詢問(wèn)題(讀擴(kuò)散問(wèn)題)

問(wèn)題說(shuō)明: 進(jìn)行分表后,對(duì)于非partition key的查詢就由為的復(fù)雜,因?yàn)榉莗artition key和partition key沒(méi)有任何關(guān)聯(lián)如果沒(méi)有采取任何措施的話,查詢效率就會(huì)十分低下。最簡(jiǎn)單的例子就是上文600w那條數(shù)據(jù),他記錄著一個(gè)用戶的個(gè)人信息,假如我們希望通過(guò)用戶名name進(jìn)行查詢,又該如何定位到這條數(shù)據(jù)呢?很明顯在沒(méi)有任何措施的情況下,只能通過(guò)逐表遍歷查詢解決了。

解決方案:

(1) 映射法:對(duì)此我們提出第一種解決方案——映射法,即通過(guò)建立一張中間表將partition key和非partition key進(jìn)行關(guān)聯(lián),以上面的例子,我們想通過(guò)name進(jìn)行查詢時(shí),可直接通過(guò)映射表帶入對(duì)應(yīng)的name,從而得到對(duì)應(yīng)的id,進(jìn)而根據(jù)id得到對(duì)應(yīng)的表即進(jìn)行查詢了。

映射算法也存在一定的缺陷,其一為了查詢要同時(shí)維護(hù)兩套表,并且普通索引更新時(shí)對(duì)應(yīng)的映射表也得更新,而且一旦數(shù)據(jù)量逐漸增大時(shí),可能還需要對(duì)映射表進(jìn)行水平拆分,再一次增加的業(yè)務(wù)實(shí)現(xiàn)的復(fù)雜度。

(2) Elasticsearch:上述的映射表起始就是一種倒排索引的思想,而ES天生就是做這種事情的,針對(duì)當(dāng)前問(wèn)題,我們直接集成ES,通過(guò)開(kāi)源工具canal監(jiān)聽(tīng)MySQL的binlog拿到日志變更,將數(shù)據(jù)采集到ES中,通過(guò)ES近乎實(shí)時(shí)查詢能力即可完美解決上述問(wèn)題。

(3) 最終方案:這些做法要么會(huì)增加維護(hù)的困難和復(fù)雜度,亦或者需要增加新的中間件,還需要為了考慮可靠性增加更多的硬件資源。所以,如果業(yè)務(wù)允許的情況下,針對(duì)這種大數(shù)據(jù)存儲(chǔ),我們更建議直接采用TIDB進(jìn)行數(shù)據(jù)存儲(chǔ),它是成熟的分布式數(shù)據(jù)存儲(chǔ)數(shù)據(jù)庫(kù),它通過(guò)引入range的概念對(duì)數(shù)據(jù)表進(jìn)行分片,有點(diǎn)類似于range范圍分表,且支持普通索引分片類似倒排索引。且其語(yǔ)法和MySQL幾乎一樣,市面也有很多工具可以輔助完成數(shù)據(jù)遷移,如果項(xiàng)目允許的話,很明顯這套數(shù)據(jù)庫(kù)是最干凈利落的解決方案了。

6. 分庫(kù)分表擴(kuò)容問(wèn)題

(1) 問(wèn)題簡(jiǎn)介:

因?yàn)楦鞣N原有我們需要對(duì)舊有數(shù)據(jù)表進(jìn)行擴(kuò)容,對(duì)此數(shù)據(jù)遷移就是一個(gè)很麻煩的問(wèn)題,有沒(méi)有什么比較安全且易實(shí)現(xiàn)的方案呢?這里筆者為大家推薦兩種比較常見(jiàn)的解決方案。

(2) 解決方案:

升級(jí)從庫(kù):先說(shuō)說(shuō)升級(jí)從庫(kù)法,這種方式就是通過(guò)升級(jí)從庫(kù)為主庫(kù)的方式實(shí)現(xiàn)數(shù)據(jù)遷移再改造hash的遷移方式。 舉個(gè)例子,假設(shè)我們現(xiàn)在有兩個(gè)分庫(kù),每個(gè)庫(kù)中有一張分表,對(duì)應(yīng)的分庫(kù)分表算法即id%2得到庫(kù)索引,然后將數(shù)據(jù)存入對(duì)應(yīng)分庫(kù)的分表中,例如我們現(xiàn)在要存儲(chǔ)一個(gè)id為600w的數(shù)據(jù),通過(guò)算法得到值為0,那么這條數(shù)據(jù)就存入分庫(kù)0的tb表,對(duì)應(yīng)的我們的從庫(kù)也跟隨db0做數(shù)據(jù)同步。

當(dāng)現(xiàn)有主庫(kù)數(shù)據(jù)已達(dá)到一定體量導(dǎo)致查詢性能下降,我們可直接將各自的從庫(kù)升級(jí)為主庫(kù),這是第一步。

完成升級(jí)從庫(kù)為主庫(kù)之后,db0對(duì)應(yīng)的從庫(kù)變?yōu)閐b2,此時(shí)這兩個(gè)數(shù)據(jù)的數(shù)據(jù)表是重復(fù)的,因?yàn)槲覀儗⒎直硭惴ㄐ薷臑閕d%4,所以我們需要基于這個(gè)算法清除冗余數(shù)據(jù),即主庫(kù)0刪除id%4=2(這些是升級(jí)為主庫(kù)的db2數(shù)據(jù)),db1刪除id%4=3(這個(gè)是升級(jí)為主庫(kù)的db3的數(shù)據(jù)),其余兩個(gè)從庫(kù)同理,自此完成算法和數(shù)據(jù)遷移的升級(jí)。

雙寫擴(kuò)容:雙寫擴(kuò)容是現(xiàn)如今比較常見(jiàn)的方案,步驟為:

  • 設(shè)計(jì)一套全新的算法的分庫(kù)分表將新的數(shù)據(jù)插入到新表中。
  • 通過(guò)同步雙寫將新數(shù)據(jù)插入新老兩庫(kù)。
  • 通過(guò)異步的方式查詢老庫(kù)的數(shù)據(jù)全部寫到新表中。
  • 完成遷移工作后以老庫(kù)為準(zhǔn)核對(duì)數(shù)據(jù),核對(duì)結(jié)束后配置關(guān)閉雙寫,后續(xù)的數(shù)據(jù)都寫入新庫(kù)。

這種方案相較于前者更加穩(wěn)妥,也是筆者較為推薦的一種解決方案。

7. 分頁(yè)查詢

將單表進(jìn)行水平維度的分庫(kù)分表之后所導(dǎo)致的庫(kù)源不一致,傳統(tǒng)的limit查詢就無(wú)法針對(duì)整個(gè)分布式維度的分頁(yè),此時(shí)我們不得不借助一些第三方工具類將庫(kù)源抽象成一個(gè)維度進(jìn)行實(shí)現(xiàn)分表查詢,我們以sharding-jdbc為例,它的做法就說(shuō)基于當(dāng)前查詢的頁(yè)數(shù)n,到所有庫(kù)源中查詢前n頁(yè)的數(shù)據(jù)并聚合,將分布式庫(kù)源檢索結(jié)果聚合成一個(gè)維度,然后進(jìn)行排序從而得到實(shí)際上的第二頁(yè)的數(shù)據(jù)并返回。

例如,我們的分庫(kù)分表希望查到第二頁(yè)的數(shù)據(jù),按照sharding-jdbc的做法,它就會(huì)將所有庫(kù)表的前2頁(yè)的數(shù)據(jù)查出來(lái),然后進(jìn)行歸并排序得到一個(gè)完整維度的前2頁(yè)的數(shù)據(jù),最后再篩選出第二頁(yè)數(shù)據(jù)返回給用戶:

但是這種做法也存在一個(gè)指明的缺陷,即深分頁(yè)數(shù)據(jù)的檢索,按照這張方案的做法,假設(shè)我們查詢100w頁(yè)的數(shù)據(jù)10條,那么我們就需要?dú)w并n表*100w頁(yè)*10條的數(shù)據(jù)進(jìn)行歸并排序,這意味著我們的程序的內(nèi)存很大概率會(huì)被打穿。

針對(duì)sharding-jdbc,感興趣的讀者可以參考筆者這篇文章,這里面針對(duì)跨庫(kù)表分頁(yè)查詢有著相對(duì)詳細(xì)的介紹:

本質(zhì)上,跨庫(kù)表分頁(yè)查詢就是因?yàn)樯⒘械臄?shù)據(jù)缺少一個(gè)全局視角,針對(duì)該問(wèn)題業(yè)界也有一個(gè)在業(yè)務(wù)和性能上相對(duì)折中的方案——二次查詢法,下面筆者就以一個(gè)單表進(jìn)行水平分庫(kù)分表后的查詢?yōu)槔菔疽幌逻@套方案。

默認(rèn)情況下,在單庫(kù)單表的情況下,我們查詢第二頁(yè)的數(shù)據(jù)4條對(duì)應(yīng)的SQL為:

select * from tb offset 4 limit 4

對(duì)應(yīng)的查詢結(jié)果如下圖所示,即id在5~8這個(gè)區(qū)間的數(shù)據(jù):

在進(jìn)行水平拆分后,得到兩張分表如下所示,接下來(lái)筆者就演示一下如何基于二次查詢實(shí)現(xiàn)相對(duì)簡(jiǎn)單且高效的數(shù)據(jù)檢索:

我們需要對(duì)這條SQL進(jìn)行改造以保證后續(xù)步驟能夠準(zhǔn)確獲取全局視角,首先我們假設(shè)分表散列均勻,所以均攤一個(gè)offset即可兩張表對(duì)應(yīng)的SQL為:

select * from tb_1 offset 2 limit 4
select * from tb_2 offset 2 limit 4

于是我們就得到了下面這張圖中綠色區(qū)間的數(shù)據(jù):

基于上述檢索到的數(shù)據(jù)進(jìn)行排序,我們得到id的最小值為4,基于這個(gè)最小值我們進(jìn)行第二次查詢,對(duì)應(yīng)的查詢采用范圍查詢的方式以排序的最小值作為起點(diǎn),當(dāng)前表的最大值作為終點(diǎn):

select * from tb_1 where id between 4 and  12
select * from tb_2 where id  between 4 and 11

可以看到分表1數(shù)據(jù)區(qū)間不變,分表2多了一條數(shù)據(jù)5,此時(shí)我們就可以基于這份樣本得到數(shù)值4在全局視角的offset值:

  • tb_1中的數(shù)值4是offset 2即偏移2得來(lái)的數(shù)據(jù),這意味著小于4的數(shù)值有兩個(gè)。
  • tb_2中的二次查詢后找到大于等于4的區(qū)間是通過(guò)offset 1得來(lái)的,這意味著小于4的數(shù)值只有1個(gè)。

由此可得數(shù)值在全局視角是offset 2+1即offset 3的結(jié)果。

基于此結(jié)果可知,我們還需要偏移一條數(shù)據(jù)即可完成offset 4的偏移,因?yàn)閿?shù)值4在全局是offset 3的結(jié)果,所以跳過(guò)數(shù)值4就可以實(shí)現(xiàn)offset 4,于是我們可知數(shù)值5開(kāi)始之后4個(gè)元素就是全局視角的第二頁(yè)的數(shù)值結(jié)果,由此查詢出5~8,二次查詢法完成:

可以看到,二次查詢大體步驟為:

  • 均衡偏移獲取候選數(shù)據(jù)。
  • 獲得最小數(shù)據(jù)作為二次查詢檢索范圍。
  • 基于二次查詢結(jié)果獲得最小值在全局的偏移量。
  • 將二次查詢結(jié)果排序結(jié)合最小值在全局視角的偏移量得出最終得出分表后的分頁(yè)結(jié)果。

這種做法無(wú)論是在性能還是實(shí)現(xiàn)復(fù)雜度都做了較好的折中,算是比較不錯(cuò)的解決方案。

8. 分布式事務(wù)

因?yàn)樗酵卣故沟脦?kù)源可能分布在不同的服務(wù)器上,所以系統(tǒng)在進(jìn)行多表操作的時(shí)候無(wú)法保證數(shù)據(jù)操作的ACID,此時(shí)我們就必須借助一個(gè)第三方工具來(lái)統(tǒng)一管理分布式數(shù)據(jù)源,常見(jiàn)的方案又seata或者rocketMQ。

我們就以seata為例了解一下主流的分布式事務(wù)解決思路:

  • seata首先會(huì)針對(duì)分布式庫(kù)源要操作的數(shù)據(jù)進(jìn)行前置鏡像備份。
  • 協(xié)調(diào)分布式庫(kù)源執(zhí)行本地事務(wù)。
  • 基于分布式庫(kù)源事務(wù)結(jié)果判斷事務(wù)是否提交。
  • 如果某個(gè)庫(kù)源事務(wù)失敗,則通知其他庫(kù)源一并回滾,反之統(tǒng)一提交。

可以看出針對(duì)分庫(kù)分表后的分布式事務(wù)本質(zhì)上就是通過(guò)第三方的工具在邏輯上的統(tǒng)一協(xié)調(diào)來(lái)保證分布式事務(wù)的ACID:

9. 在分庫(kù)分表后的模糊查詢

針對(duì)大庫(kù)表進(jìn)行分庫(kù)分表分散壓力之后,針對(duì)下面這種非前綴匹配還是顯得有些力不從心(索引失效):

select * from tb where name like '%ming%'

對(duì)于此問(wèn)題最好的辦法就是專業(yè)事情讓專業(yè)的工具解決,我們完全可以通過(guò)bin.log訂閱分庫(kù)分表數(shù)據(jù)將其提交到elasticSearch中,通過(guò)其強(qiáng)大的自然語(yǔ)言處理的分詞器和倒排索引這種天生為數(shù)據(jù)檢索而生的設(shè)計(jì)理念來(lái)解決模糊搜索問(wèn)題:

10. 分庫(kù)分表如何進(jìn)行庫(kù)表資源評(píng)估

  • 確定數(shù)據(jù)表體量以及TPS
  • 針對(duì)TPS進(jìn)行分庫(kù)
  • 基于分庫(kù)后得到單庫(kù)的數(shù)據(jù)體量進(jìn)行分表

例如我們現(xiàn)在TPS為6000,每日數(shù)據(jù)體量在2e,按照上述步驟的推算過(guò)程為:

  • 按照先常見(jiàn)的硬件配置對(duì)MySQL實(shí)例(8C32G)的壓測(cè),單庫(kù)TPS基本在2000~3000,按照我們業(yè)務(wù)體量TPS需求為6000,大體需要3個(gè)庫(kù),當(dāng)然我們也可以適當(dāng)冗余一個(gè)庫(kù)預(yù)防流量突增,所以最終我們分配4個(gè)庫(kù)。
  • 平均之后單庫(kù)數(shù)據(jù)差不多在7000w以內(nèi)(2e/3),按照業(yè)界標(biāo)準(zhǔn)單條數(shù)據(jù)1k的情況下單表最好在2000w以內(nèi),所以我們單庫(kù)標(biāo)準(zhǔn)分3張表,還是同樣道理冗余1張預(yù)防突增的數(shù)據(jù),也可以不用,因?yàn)槲覀円呀?jīng)適當(dāng)冗余了一個(gè)數(shù)據(jù)庫(kù)。

基于4庫(kù)3表指定分庫(kù)分表方案并完成業(yè)務(wù)落地:


責(zé)任編輯:趙寧寧 來(lái)源: 寫代碼的SharkChili
相關(guān)推薦

2024-05-06 00:30:00

MVCC數(shù)據(jù)庫(kù)

2022-03-23 15:36:13

數(shù)字化轉(zhuǎn)型數(shù)據(jù)治理企業(yè)

2023-08-28 07:39:49

線程調(diào)度基本單位

2024-08-13 17:09:00

架構(gòu)分庫(kù)分表開(kāi)發(fā)

2022-07-11 08:16:47

NewSQL關(guān)系數(shù)據(jù)庫(kù)系統(tǒng)

2022-12-09 09:21:10

分庫(kù)分表算法

2020-07-30 17:59:34

分庫(kù)分表SQL數(shù)據(jù)庫(kù)

2019-11-12 09:54:20

分庫(kù)分表數(shù)據(jù)

2017-05-02 17:22:48

2023-09-26 00:45:11

MySQL拆表分片

2020-06-24 09:00:43

分庫(kù)分表MySQL

2022-04-02 14:51:58

數(shù)據(jù)中心數(shù)據(jù)安全物聯(lián)網(wǎng)

2024-04-16 16:20:46

2024-09-09 12:00:34

2025-04-09 00:00:00

2024-07-25 18:20:03

2021-08-31 20:21:11

VitessMySQL分庫(kù)

2023-08-11 08:59:49

分庫(kù)分表數(shù)據(jù)數(shù)據(jù)庫(kù)

2022-06-02 16:04:55

分庫(kù)表數(shù)據(jù)

2020-11-18 09:39:02

MySQL數(shù)據(jù)庫(kù)SQL
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)