溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

mysql數(shù)據(jù)庫(kù)分庫(kù)分表講解

發(fā)布時(shí)間:2021-09-01 19:57:46 來(lái)源:億速云 閱讀:189 作者:chen 欄目:大數(shù)據(jù)

本篇內(nèi)容介紹了“mysql數(shù)據(jù)庫(kù)分庫(kù)分表講解”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

今天我來(lái)介紹常見(jiàn)的分散存儲(chǔ)的方法“分庫(kù)分表”,其中包括“分庫(kù)”和“分表”兩大類(lèi)。

業(yè)務(wù)分庫(kù)

業(yè)務(wù)分庫(kù)指的是按照業(yè)務(wù)模塊將數(shù)據(jù)分散到不同的數(shù)據(jù)庫(kù)服務(wù)器例如,一個(gè)簡(jiǎn)單的電商網(wǎng)站,包括用戶、商品、訂單三個(gè)業(yè)務(wù)模塊,我們可以將用戶數(shù)據(jù)、商品數(shù)據(jù)、訂單數(shù)據(jù)分開(kāi)放到三臺(tái)不同的數(shù)據(jù)庫(kù)服務(wù)器上,而不是將所有數(shù)據(jù)都放在一臺(tái)數(shù)據(jù)庫(kù)服務(wù)器上。

mysql數(shù)據(jù)庫(kù)分庫(kù)分表講解

雖然業(yè)務(wù)分庫(kù)能夠分散存儲(chǔ)和訪問(wèn)壓力,但同時(shí)也帶來(lái)了新的問(wèn)題,接下來(lái)我進(jìn)行詳細(xì)分析。

1.join 操作問(wèn)題

業(yè)務(wù)分庫(kù)后,原本在同一個(gè)數(shù)據(jù)庫(kù)中的表分散到不同數(shù)據(jù)庫(kù)中,導(dǎo)致無(wú)法使用 SQL 的 join 查詢。

例如:“查詢購(gòu)買(mǎi)了化妝品的用戶中女性用戶的列表”這個(gè)功能,雖然訂單數(shù)據(jù)中有用戶的 ID 信息,但是用戶的性別數(shù)據(jù)在用戶數(shù)據(jù)庫(kù)中,如果在同一個(gè)庫(kù)中,簡(jiǎn)單的 join 查詢就能完成;但現(xiàn)在數(shù)據(jù)分散在兩個(gè)不同的數(shù)據(jù)庫(kù)中,無(wú)法做 join 查詢,只能采取先從訂單數(shù)據(jù)庫(kù)中查詢購(gòu)買(mǎi)了化妝品的用戶 ID 列表,然后再到用戶數(shù)據(jù)庫(kù)中查詢這批用戶 ID 中的女性用戶列表,這樣實(shí)現(xiàn)就比簡(jiǎn)單的 join 查詢要復(fù)雜一些。

2. 事務(wù)問(wèn)題

原本在同一個(gè)數(shù)據(jù)庫(kù)中不同的表可以在同一個(gè)事務(wù)中修改,業(yè)務(wù)分庫(kù)后,表分散到不同的數(shù)據(jù)庫(kù)中,無(wú)法通過(guò)事務(wù)統(tǒng)一修改。雖然數(shù)據(jù)庫(kù)廠商提供了一些分布式事務(wù)的解決方案(例如,MySQL 的 XA),但性能實(shí)在太低,與高性能存儲(chǔ)的目標(biāo)是相違背的。

例如,用戶下訂單的時(shí)候需要扣商品庫(kù)存,如果訂單數(shù)據(jù)和商品數(shù)據(jù)在同一個(gè)數(shù)據(jù)庫(kù)中,我們可以使用事務(wù)來(lái)保證扣減商品庫(kù)存和生成訂單的操作要么都成功要么都失敗,但分庫(kù)后就無(wú)法使用數(shù)據(jù)庫(kù)事務(wù)了,需要業(yè)務(wù)程序自己來(lái)模擬實(shí)現(xiàn)事務(wù)的功能。例如,先扣商品庫(kù)存,扣成功后生成訂單,如果因?yàn)橛唵螖?shù)據(jù)庫(kù)異常導(dǎo)致生成訂單失敗,業(yè)務(wù)程序又需要將商品庫(kù)存加上;而如果因?yàn)闃I(yè)務(wù)程序自己異常導(dǎo)致生成訂單失敗,則商品庫(kù)存就無(wú)法恢復(fù)了,需要人工通過(guò)日志等方式來(lái)手工修復(fù)庫(kù)存異常。

3. 成本問(wèn)題

業(yè)務(wù)分庫(kù)同時(shí)也帶來(lái)了成本的代價(jià),本來(lái) 1 臺(tái)服務(wù)器搞定的事情,現(xiàn)在要 3 臺(tái),如果考慮備份,那就是 2 臺(tái)變成了 6 臺(tái)。

基于上述原因,對(duì)于小公司初創(chuàng)業(yè)務(wù),并不建議一開(kāi)始就這樣拆分,主要有幾個(gè)原因:

  • 初創(chuàng)業(yè)務(wù)存在很大的不確定性,業(yè)務(wù)不一定能發(fā)展起來(lái),業(yè)務(wù)開(kāi)始的時(shí)候并沒(méi)有真正的存儲(chǔ)和訪問(wèn)壓力,業(yè)務(wù)分庫(kù)并不能為業(yè)務(wù)帶來(lái)價(jià)值。

  • 業(yè)務(wù)分庫(kù)后,表之間的 join 查詢、數(shù)據(jù)庫(kù)事務(wù)無(wú)法簡(jiǎn)單實(shí)現(xiàn)了。

  • 業(yè)務(wù)分庫(kù)后,因?yàn)椴煌臄?shù)據(jù)要讀寫(xiě)不同的數(shù)據(jù)庫(kù),代碼中需要增加根據(jù)數(shù)據(jù)類(lèi)型映射到不同數(shù)據(jù)庫(kù)的邏輯,增加了工作量。而業(yè)務(wù)初創(chuàng)期間最重要的是快速實(shí)現(xiàn)、快速驗(yàn)證,業(yè)務(wù)分庫(kù)會(huì)拖慢業(yè)務(wù)節(jié)奏。

有的架構(gòu)師可能會(huì)想:如果業(yè)務(wù)真的發(fā)展很快,豈不是很快就又要進(jìn)行業(yè)務(wù)分庫(kù)了?那為何不一開(kāi)始就設(shè)計(jì)好呢?

其實(shí)這個(gè)問(wèn)題很好回答,按照我前面提到的“架構(gòu)設(shè)計(jì)三原則”,簡(jiǎn)單分析一下。

首先,這里的“如果”事實(shí)上發(fā)生的概率比較低,做 10 個(gè)業(yè)務(wù)有 1 個(gè)業(yè)務(wù)能活下去就很不錯(cuò)了,更何況快速發(fā)展,和中彩票的概率差不多。如果我們每個(gè)業(yè)務(wù)上來(lái)就按照淘寶、微信的規(guī)模去做架構(gòu)設(shè)計(jì),不但會(huì)累死自己,還會(huì)害死業(yè)務(wù)。

其次,如果業(yè)務(wù)真的發(fā)展很快,后面進(jìn)行業(yè)務(wù)分庫(kù)也不遲。因?yàn)闃I(yè)務(wù)發(fā)展好,相應(yīng)的資源投入就會(huì)加大,可以投入更多的人和更多的錢(qián),那業(yè)務(wù)分庫(kù)帶來(lái)的代碼和業(yè)務(wù)復(fù)雜的問(wèn)題就可以通過(guò)增加人來(lái)解決,成本問(wèn)題也可以通過(guò)增加資金來(lái)解決。

第三,單臺(tái)數(shù)據(jù)庫(kù)服務(wù)器的性能其實(shí)也沒(méi)有想象的那么弱,一般來(lái)說(shuō),單臺(tái)數(shù)據(jù)庫(kù)服務(wù)器能夠支撐 10 萬(wàn)用戶量量級(jí)的業(yè)務(wù),初創(chuàng)業(yè)務(wù)從 0 發(fā)展到 10 萬(wàn)級(jí)用戶,并不是想象得那么快。

而對(duì)于業(yè)界成熟的大公司來(lái)說(shuō),由于已經(jīng)有了業(yè)務(wù)分庫(kù)的成熟解決方案,并且即使是嘗試性的新業(yè)務(wù),用戶規(guī)模也是海量的,這與前面提到的初創(chuàng)業(yè)務(wù)的小公司有本質(zhì)區(qū)別,因此最好在業(yè)務(wù)開(kāi)始設(shè)計(jì)時(shí)就考慮業(yè)務(wù)分庫(kù)。例如,在淘寶上做一個(gè)新的業(yè)務(wù),由于已經(jīng)有成熟的數(shù)據(jù)庫(kù)解決方案,用戶量也很大,需要在一開(kāi)始就設(shè)計(jì)業(yè)務(wù)分庫(kù)甚至接下來(lái)介紹的分表方案。

分表

將不同業(yè)務(wù)數(shù)據(jù)分散存儲(chǔ)到不同的數(shù)據(jù)庫(kù)服務(wù)器,能夠支撐百萬(wàn)甚至千萬(wàn)用戶規(guī)模的業(yè)務(wù),但如果業(yè)務(wù)繼續(xù)發(fā)展,同一業(yè)務(wù)的單表數(shù)據(jù)也會(huì)達(dá)到單臺(tái)數(shù)據(jù)庫(kù)服務(wù)器的處理瓶頸。例如,淘寶的幾億用戶數(shù)據(jù),如果全部存放在一臺(tái)數(shù)據(jù)庫(kù)服務(wù)器的一張表中,肯定是無(wú)法滿足性能要求的,此時(shí)就需要對(duì)單表數(shù)據(jù)進(jìn)行拆分。

單表數(shù)據(jù)拆分有兩種方式:垂直分表水平分表。示意圖如下:

mysql數(shù)據(jù)庫(kù)分庫(kù)分表講解??

為了形象地理解垂直拆分和水平拆分的區(qū)別,可以想象你手里拿著一把刀,面對(duì)一個(gè)蛋糕切一刀:

  • 從上往下切就是垂直切分,因?yàn)榈兜倪\(yùn)行軌跡與蛋糕是垂直的,這樣可以把蛋糕切成高度相等(面積可以相等也可以不相等)的兩部分,對(duì)應(yīng)到表的切分就是表記錄數(shù)相同但包含不同的列。例如,示意圖中的垂直切分,會(huì)把表切分為兩個(gè)表,一個(gè)表包含 ID、name、age、sex 列,另外一個(gè)表包含 ID、nickname、description 列。

  • 從左往右切就是水平切分,因?yàn)榈兜倪\(yùn)行軌跡與蛋糕是平行的,這樣可以把蛋糕切成面積相等(高度可以相等也可以不相等)的兩部分,對(duì)應(yīng)到表的切分就是表的列相同但包含不同的行數(shù)據(jù)。例如,示意圖中的水平切分,會(huì)把表分為兩個(gè)表,兩個(gè)表都包含 ID、name、age、sex、nickname、description 列,但是一個(gè)表包含的是 ID 從 1 到 999999 的行數(shù)據(jù),另一個(gè)表包含的是 ID 從 1000000 到 9999999 的行數(shù)據(jù)。

上面這個(gè)示例比較簡(jiǎn)單,只考慮了一次切分的情況,實(shí)際架構(gòu)設(shè)計(jì)過(guò)程中并不局限切分的次數(shù),可以切兩次,也可以切很多次,就像切蛋糕一樣,可以切很多刀。

單表進(jìn)行切分后,是否要將切分后的多個(gè)表分散在不同的數(shù)據(jù)庫(kù)服務(wù)器中,可以根據(jù)實(shí)際的切分效果來(lái)確定,并不強(qiáng)制要求單表切分為多表后一定要分散到不同數(shù)據(jù)庫(kù)中。原因在于單表切分為多表后,新的表即使在同一個(gè)數(shù)據(jù)庫(kù)服務(wù)器中,也可能帶來(lái)可觀的性能提升,如果性能能夠滿足業(yè)務(wù)要求,是可以不拆分到多臺(tái)數(shù)據(jù)庫(kù)服務(wù)器的,畢竟我們?cè)谏厦鏄I(yè)務(wù)分庫(kù)的內(nèi)容看到業(yè)務(wù)分庫(kù)也會(huì)引入很多復(fù)雜性的問(wèn)題;如果單表拆分為多表后,單臺(tái)服務(wù)器依然無(wú)法滿足性能要求,那就不得不再次進(jìn)行業(yè)務(wù)分庫(kù)的設(shè)計(jì)了。

分表能夠有效地分散存儲(chǔ)壓力和帶來(lái)性能提升,但和分庫(kù)一樣,也會(huì)引入各種復(fù)雜性。

1. 垂直分表

垂直分表適合將表中某些不常用且占了大量空間的列拆分出去。例如,前面示意圖中的 nickname 和 description 字段,假設(shè)我們是一個(gè)婚戀網(wǎng)站,用戶在篩選其他用戶的時(shí)候,主要是用 age 和 sex 兩個(gè)字段進(jìn)行查詢,而 nickname 和 description 兩個(gè)字段主要用于展示,一般不會(huì)在業(yè)務(wù)查詢中用到。description 本身又比較長(zhǎng),因此我們可以將這兩個(gè)字段獨(dú)立到另外一張表中,這樣在查詢 age 和 sex 時(shí),就能帶來(lái)一定的性能提升。

垂直分表引入的復(fù)雜性主要體現(xiàn)在表操作的數(shù)量要增加。例如,原來(lái)只要一次查詢就可以獲取 name、age、sex、nickname、description,現(xiàn)在需要兩次查詢,一次查詢獲取 name、age、sex,另外一次查詢獲取 nickname、description。

不過(guò)相比接下來(lái)要講的水平分表,這個(gè)復(fù)雜性就是小巫見(jiàn)大巫了。

2. 水平分表

水平分表適合表行數(shù)特別大的表,有的公司要求單表行數(shù)超過(guò) 5000 萬(wàn)就必須進(jìn)行分表,這個(gè)數(shù)字可以作為參考,但并不是絕對(duì)標(biāo)準(zhǔn),關(guān)鍵還是要看表的訪問(wèn)性能。對(duì)于一些比較復(fù)雜的表,可能超過(guò) 1000 萬(wàn)就要分表了;而對(duì)于一些簡(jiǎn)單的表,即使存儲(chǔ)數(shù)據(jù)超過(guò) 1 億行,也可以不分表。但不管怎樣,當(dāng)看到表的數(shù)據(jù)量達(dá)到千萬(wàn)級(jí)別時(shí),作為架構(gòu)師就要警覺(jué)起來(lái),因?yàn)檫@很可能是架構(gòu)的性能瓶頸或者隱患。

水平分表相比垂直分表,會(huì)引入更多的復(fù)雜性,主要表現(xiàn)在下面幾個(gè)方面:

  • 路由

水平分表后,某條數(shù)據(jù)具體屬于哪個(gè)切分后的子表,需要增加路由算法進(jìn)行計(jì)算,這個(gè)算法會(huì)引入一定的復(fù)雜性。

常見(jiàn)的路由算法有:

范圍路由:選取有序的數(shù)據(jù)列(例如,整形、時(shí)間戳等)作為路由的條件,不同分段分散到不同的數(shù)據(jù)庫(kù)表中。以最常見(jiàn)的用戶 ID 為例,路由算法可以按照 1000000 的范圍大小進(jìn)行分段,1 ~ 999999 放到數(shù)據(jù)庫(kù) 1 的表中,1000000 ~ 1999999 放到數(shù)據(jù)庫(kù) 2 的表中,以此類(lèi)推。

范圍路由設(shè)計(jì)的復(fù)雜點(diǎn)主要體現(xiàn)在分段大小的選取上,分段太小會(huì)導(dǎo)致切分后子表數(shù)量過(guò)多,增加維護(hù)復(fù)雜度;分段太大可能會(huì)導(dǎo)致單表依然存在性能問(wèn)題,一般建議分段大小在 100 萬(wàn)至 2000 萬(wàn)之間,具體需要根據(jù)業(yè)務(wù)選取合適的分段大小。

范圍路由的優(yōu)點(diǎn)是可以隨著數(shù)據(jù)的增加平滑地?cái)U(kuò)充新的表。例如,現(xiàn)在的用戶是 100 萬(wàn),如果增加到 1000 萬(wàn),只需要增加新的表就可以了,原有的數(shù)據(jù)不需要?jiǎng)印?/p>

范圍路由的一個(gè)比較隱含的缺點(diǎn)是分布不均勻,假如按照 1000 萬(wàn)來(lái)進(jìn)行分表,有可能某個(gè)分段實(shí)際存儲(chǔ)的數(shù)據(jù)量只有 1000 條,而另外一個(gè)分段實(shí)際存儲(chǔ)的數(shù)據(jù)量有 900 萬(wàn)條。

Hash 路由:選取某個(gè)列(或者某幾個(gè)列組合也可以)的值進(jìn)行 Hash 運(yùn)算,然后根據(jù) Hash 結(jié)果分散到不同的數(shù)據(jù)庫(kù)表中。同樣以用戶 ID 為例,假如我們一開(kāi)始就規(guī)劃了 10 個(gè)數(shù)據(jù)庫(kù)表,路由算法可以簡(jiǎn)單地用 user_id % 10 的值來(lái)表示數(shù)據(jù)所屬的數(shù)據(jù)庫(kù)表編號(hào),ID 為 985 的用戶放到編號(hào)為 5 的子表中,ID 為 10086 的用戶放到編號(hào)為 6 的字表中。

Hash 路由設(shè)計(jì)的復(fù)雜點(diǎn)主要體現(xiàn)在初始表數(shù)量的選取上,表數(shù)量太多維護(hù)比較麻煩,表數(shù)量太少又可能導(dǎo)致單表性能存在問(wèn)題。而用了 Hash 路由后,增加字表數(shù)量是非常麻煩的,所有數(shù)據(jù)都要重分布。

Hash 路由的優(yōu)缺點(diǎn)和范圍路由基本相反,Hash 路由的優(yōu)點(diǎn)是表分布比較均勻,缺點(diǎn)是擴(kuò)充新的表很麻煩,所有數(shù)據(jù)都要重分布。

配置路由:配置路由就是路由表,用一張獨(dú)立的表來(lái)記錄路由信息。同樣以用戶 ID 為例,我們新增一張 user_router 表,這個(gè)表包含 user_id 和 table_id 兩列,根據(jù) user_id 就可以查詢對(duì)應(yīng)的 table_id。

配置路由設(shè)計(jì)簡(jiǎn)單,使用起來(lái)非常靈活,尤其是在擴(kuò)充表的時(shí)候,只需要遷移指定的數(shù)據(jù),然后修改路由表就可以了。

配置路由的缺點(diǎn)就是必須多查詢一次,會(huì)影響整體性能;而且路由表本身如果太大(例如,幾億條數(shù)據(jù)),性能同樣可能成為瓶頸,如果我們?cè)俅螌⒙酚杀矸謳?kù)分表,則又面臨一個(gè)死循環(huán)式的路由算法選擇問(wèn)題。

  • join 操作

水平分表后,數(shù)據(jù)分散在多個(gè)表中,如果需要與其他表進(jìn)行 join 查詢,需要在業(yè)務(wù)代碼或者數(shù)據(jù)庫(kù)中間件中進(jìn)行多次 join 查詢,然后將結(jié)果合并。

  • count() 操作

水平分表后,雖然物理上數(shù)據(jù)分散到多個(gè)表中,但某些業(yè)務(wù)邏輯上還是會(huì)將這些表當(dāng)作一個(gè)表來(lái)處理。例如,獲取記錄總數(shù)用于分頁(yè)或者展示,水平分表前用一個(gè) count() 就能完成的操作,在分表后就沒(méi)那么簡(jiǎn)單了。常見(jiàn)的處理方式有下面兩種:

count() 相加:具體做法是在業(yè)務(wù)代碼或者數(shù)據(jù)庫(kù)中間件中對(duì)每個(gè)表進(jìn)行 count() 操作,然后將結(jié)果相加。這種方式實(shí)現(xiàn)簡(jiǎn)單,缺點(diǎn)就是性能比較低。例如,水平分表后切分為 20 張表,則要進(jìn)行 20 次 count(*) 操作,如果串行的話,可能需要幾秒鐘才能得到結(jié)果。

記錄數(shù)表:具體做法是新建一張表,假如表名為“記錄數(shù)表”,包含 table_name、row_count 兩個(gè)字段,每次插入或者刪除子表數(shù)據(jù)成功后,都更新“記錄數(shù)表”。

這種方式獲取表記錄數(shù)的性能要大大優(yōu)于 count() 相加的方式,因?yàn)橹恍枰淮魏?jiǎn)單查詢就可以獲取數(shù)據(jù)。缺點(diǎn)是復(fù)雜度增加不少,對(duì)子表的操作要同步操作“記錄數(shù)表”,如果有一個(gè)業(yè)務(wù)邏輯遺漏了,數(shù)據(jù)就會(huì)不一致;且針對(duì)“記錄數(shù)表”的操作和針對(duì)子表的操作無(wú)法放在同一事務(wù)中進(jìn)行處理,異常的情況下會(huì)出現(xiàn)操作子表成功了而操作記錄數(shù)表失敗,同樣會(huì)導(dǎo)致數(shù)據(jù)不一致。

此外,記錄數(shù)表的方式也增加了數(shù)據(jù)庫(kù)的寫(xiě)壓力,因?yàn)槊看吾槍?duì)子表的 insert 和 delete 操作都要 update 記錄數(shù)表,所以對(duì)于一些不要求記錄數(shù)實(shí)時(shí)保持精確的業(yè)務(wù),也可以通過(guò)后臺(tái)定時(shí)更新記錄數(shù)表。定時(shí)更新實(shí)際上就是“count() 相加”和“記錄數(shù)表”的結(jié)合,即定時(shí)通過(guò) count() 相加計(jì)算表的記錄數(shù),然后更新記錄數(shù)表中的數(shù)據(jù)。

  • order by 操作

水平分表后,數(shù)據(jù)分散到多個(gè)子表中,排序操作無(wú)法在數(shù)據(jù)庫(kù)中完成,只能由業(yè)務(wù)代碼或者數(shù)據(jù)庫(kù)中間件分別查詢每個(gè)子表中的數(shù)據(jù),然后匯總進(jìn)行排序。

實(shí)現(xiàn)方法

和數(shù)據(jù)庫(kù)讀寫(xiě)分離類(lèi)似,分庫(kù)分表具體的實(shí)現(xiàn)方式也是“程序代碼封裝”和“中間件封裝”,但實(shí)現(xiàn)會(huì)更復(fù)雜。讀寫(xiě)分離實(shí)現(xiàn)時(shí)只要識(shí)別 SQL 操作是讀操作還是寫(xiě)操作,通過(guò)簡(jiǎn)單的判斷 SELECT、UPDATE、INSERT、DELETE 幾個(gè)關(guān)鍵字就可以做到,而分庫(kù)分表的實(shí)現(xiàn)除了要判斷操作類(lèi)型外,還要判斷 SQL 中具體需要操作的表、操作函數(shù)(例如 count 函數(shù))、order by、group by 操作等,然后再根據(jù)不同的操作進(jìn)行不同的處理。例如 order by 操作,需要先從多個(gè)庫(kù)查詢到各個(gè)庫(kù)的數(shù)據(jù),然后再重新 order by 才能得到最終的結(jié)果。

“mysql數(shù)據(jù)庫(kù)分庫(kù)分表講解”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI