更新時(shí)間:2022-08-25 來(lái)源:黑馬程序員 瀏覽量:
導(dǎo)讀
各位小伙伴,在目前企業(yè)級(jí)開(kāi)發(fā)中采用Mysql做為數(shù)據(jù)庫(kù)是一個(gè)主流選擇,而當(dāng)數(shù)據(jù)量比較大的情況下,為了支撐項(xiàng)目的正??焖俚倪\(yùn)行,我們不得不選擇對(duì)數(shù)據(jù)庫(kù)分庫(kù)分表操作,本章節(jié)就對(duì)數(shù)據(jù)庫(kù)的分表做一些方案的講解,包括如下:
- 為什么要分庫(kù)分表
- 分庫(kù)分表的具體方式
- 分庫(kù)分表帶來(lái)的問(wèn)題及解決方案有哪些
2 .為什么分庫(kù)分表
隨著平臺(tái)的業(yè)務(wù)發(fā)展,數(shù)據(jù)可能會(huì)越來(lái)越多,甚至達(dá)到億級(jí)。以MySQL為例,單庫(kù)數(shù)據(jù)量在5000萬(wàn)以內(nèi)性能比較好,超過(guò)閾值后性能會(huì)隨著數(shù)據(jù)量的增大而明顯降低。單表的數(shù)據(jù)量超過(guò)1000w,性能也會(huì)下降嚴(yán)重。這就會(huì)導(dǎo)致查詢一次所花的時(shí)間變長(zhǎng),并發(fā)操作達(dá)到一定量時(shí)可能會(huì)卡死,甚至把系統(tǒng)給拖垮。
我們是否可以通過(guò)提升服務(wù)器硬件能力來(lái)提高數(shù)據(jù)處理能力?能,但是這種方案很貴,并且提高硬件是有上限的。**那我們能不能把數(shù)據(jù)分散在不同的數(shù)據(jù)庫(kù)中,使得單一數(shù)據(jù)庫(kù)和表的數(shù)據(jù)量變小,從而達(dá)到提升數(shù)據(jù)庫(kù)操作性能的目的?** 可以,這就是數(shù)據(jù)庫(kù)分庫(kù)分表。
分庫(kù)分表就是把較大的數(shù)據(jù)庫(kù)和數(shù)據(jù)表按照某種策略進(jìn)行拆分。目的在于:降低每個(gè)庫(kù)、每張表的數(shù)據(jù)量,減小數(shù)據(jù)庫(kù)的負(fù)擔(dān),提高數(shù)據(jù)庫(kù)的效率,縮短查詢時(shí)間。另外,因?yàn)榉謳?kù)分表這種改造是可控的,底層還是基于RDBMS,因此整個(gè)數(shù)據(jù)庫(kù)的運(yùn)維體系以及相關(guān)基礎(chǔ)設(shè)施都是可重用的。
3. 分庫(kù)分表的方式
3.1 垂直分表
用戶在電商平臺(tái)流覽商品時(shí),首先看到的是商品的基本信息,如果對(duì)該商品感興趣時(shí)才會(huì)繼續(xù)查看該商品的詳細(xì)描述。因此,商品基本信息的訪問(wèn)頻次要高于商品詳細(xì)描述信息,商品基本信息的訪問(wèn)效率要高于商品詳細(xì)描述信息(大字段)。 由于這兩種數(shù)據(jù)的特性不一樣,因此考慮將商品信息表拆分如下:
這種拆分就叫垂直分表。垂直分表定義:將一個(gè)表的字段分散到多個(gè)表中,每個(gè)表存儲(chǔ)其中一部分字段。垂直分表帶來(lái)的提升是:
1.減少IO爭(zhēng)搶,減少鎖表的幾率,查看商品詳情的與商品概述互不影響
2. 充分發(fā)揮高頻數(shù)據(jù)的操作效率,對(duì)商品概述數(shù)據(jù)操作的高效率不會(huì)被操作商品詳情數(shù)據(jù)的低效率所拖累。
一般來(lái)說(shuō),某業(yè)務(wù)實(shí)體中的各個(gè)數(shù)據(jù)項(xiàng)的訪問(wèn)頻次是不一樣的,部分?jǐn)?shù)據(jù)項(xiàng)可能是占用存儲(chǔ)空間比較大的BLOB或是TEXT,例如上例中的商品描述字段。所以,當(dāng)數(shù)據(jù)量很大時(shí),可以將表按字段拆分,將熱門字段、冷門字段分開(kāi)放置在不同表中。垂直切分帶來(lái)的性能提升,主要集中在熱門數(shù)據(jù)的操作效率上,而且磁盤爭(zhēng)用情況減少。通常我們按以下原則進(jìn)行垂直拆分:
- 把不常用的字段單獨(dú)放在一張表
- 把text,blob等大字段拆分出來(lái)單獨(dú)放在一張表
- 經(jīng)常組合查詢的字段單獨(dú)放在一張表中
3.2 垂直分庫(kù)
通過(guò)垂直分表,數(shù)據(jù)庫(kù)性能得到了一定程度的提升,但是還沒(méi)有達(dá)到要求,并且磁盤空間也快不夠了,因?yàn)閿?shù)據(jù)還是始終存放在一臺(tái)服務(wù)器。庫(kù)內(nèi)垂直分表只解決了單一表數(shù)據(jù)量過(guò)大的問(wèn)題,但沒(méi)有將表分布到不同機(jī)器的庫(kù)上,因此對(duì)于減輕數(shù)據(jù)庫(kù)的壓力來(lái)說(shuō),作用有限,大家還是競(jìng)爭(zhēng)同一個(gè)物理機(jī)的CPU、內(nèi)存、網(wǎng)絡(luò)IO、磁盤。
以電商平臺(tái)為例,可以把原有的SELLER_DB(賣家?guī)?,拆分為PRODUCT_DB(商品庫(kù))和STORE_DB(店鋪庫(kù)),并把這兩個(gè)庫(kù)分散到不同服務(wù)器上,如下圖所示:
由于商品信息與商品描述業(yè)務(wù)耦合度較高,因此一起被存放在PRODUCT_DB(商品庫(kù));而店鋪信息相對(duì)獨(dú)立,因此單獨(dú)被存放在STORE_DB(店鋪庫(kù)),這就叫垂直分庫(kù)。
垂直分庫(kù)是指按照業(yè)務(wù)將表進(jìn)行分類,分布到不同的數(shù)據(jù)庫(kù)上面,每個(gè)庫(kù)可以放在不同的服務(wù)器上,從而達(dá)到多個(gè)服務(wù)器共同分?jǐn)倝毫Φ男Ч?。垂直分?kù)帶來(lái)的提升是:
- 解決業(yè)務(wù)層面的耦合,業(yè)務(wù)清晰
- 能對(duì)不同業(yè)務(wù)的數(shù)據(jù)進(jìn)行分級(jí)管理、維護(hù)、監(jiān)控、擴(kuò)展等
- 高并發(fā)場(chǎng)景下,垂直分庫(kù)在一定程度上可以提升IO、數(shù)據(jù)庫(kù)連接數(shù)、單機(jī)硬件資源的性能
3.3 水平分庫(kù)
經(jīng)過(guò)**垂直分表和垂直分庫(kù)**后,數(shù)據(jù)庫(kù)性能問(wèn)題就完全解決了?假設(shè)某電商平臺(tái)發(fā)展迅猛,PRODUCT_DB(商品庫(kù))單庫(kù)存儲(chǔ)數(shù)據(jù)已經(jīng)超出預(yù)估。假設(shè)目前該平臺(tái)有8w店鋪,每個(gè)店鋪平均有150個(gè)不同規(guī)格的商品,再算上增長(zhǎng),那商品數(shù)量就會(huì)達(dá)到1500w+級(jí)別,并且PRODUCT_DB(商品庫(kù))屬于訪問(wèn)非常頻繁的資源,性能瓶頸再次出現(xiàn)。
能再次垂直分庫(kù)嗎?從業(yè)務(wù)角度分析,目前已經(jīng)無(wú)法再次垂直拆分。于是我們又想了一個(gè)辦法,判斷商品ID是奇數(shù)還是偶數(shù),然后把商品信息分別存放到兩個(gè)數(shù)據(jù)庫(kù)中。也就是說(shuō),要操作某條數(shù)據(jù),先分析這條數(shù)據(jù)的商品ID,如果商品ID為奇數(shù),將此操作映射至RRODUCT_DB1(商品庫(kù)1);如果商品ID為偶數(shù),將操作映射至RRODUCT_DB2(商品庫(kù)2),這就叫**水平分庫(kù)**。
水平分庫(kù)是把同一個(gè)表的數(shù)據(jù)按一定規(guī)則拆分到不同的數(shù)據(jù)庫(kù)中,每個(gè)庫(kù)可以放在不同的服務(wù)器上。它帶來(lái)的提升是:
- 解決了單庫(kù)大數(shù)據(jù),高并發(fā)的性能瓶頸。
- 按照合理拆分規(guī)則拆分,join操作基本避免跨庫(kù)。
- 提高了系統(tǒng)的穩(wěn)定性及可用性。
當(dāng)一個(gè)應(yīng)用難以再細(xì)粒度的垂直切分,或切分后數(shù)據(jù)量行數(shù)仍然巨大,存在單庫(kù)讀寫、存儲(chǔ)性能瓶頸,這時(shí)候就需要進(jìn)行**水平分庫(kù)**了,經(jīng)過(guò)水平切分的優(yōu)化,往往能解決單庫(kù)存儲(chǔ)量及性能瓶頸。但由于同一個(gè)表被分配在不同的數(shù)據(jù)庫(kù),需要額外進(jìn)行數(shù)據(jù)操作的路由工作,因此大大增加了系統(tǒng)復(fù)雜度。
3.4 水平分表
數(shù)據(jù)庫(kù)能水平拆分,那數(shù)據(jù)表是不是也可以呢?我們嘗試把某PRODUCT_DB(商品庫(kù))內(nèi)的表,進(jìn)行了一次水平拆分:
與水平分庫(kù)的思路類似,不過(guò)這次拆分的目標(biāo)是表,商品信息及商品描述被分成了兩套表。如果商品ID為奇數(shù),將此操作映射至商品信息1表;如果商品ID為偶數(shù),將操作映射至商品信息2表,這就叫水平分表。水平分表是在同一個(gè)數(shù)據(jù)庫(kù)內(nèi),把同一個(gè)表的數(shù)據(jù)按一定規(guī)則拆分到多個(gè)表中。它帶來(lái)的提升是:
- 優(yōu)化單一表數(shù)據(jù)量過(guò)大而產(chǎn)生的性能問(wèn)題
- 避免IO爭(zhēng)搶并減少鎖表的幾率
庫(kù)內(nèi)的水平分表,解決了單一表數(shù)據(jù)量過(guò)大的問(wèn)題,分出來(lái)的小表中只包含一部分?jǐn)?shù)據(jù),從而使得單個(gè)表的數(shù)據(jù)量變小,提高檢索性能。但由于同一個(gè)表的數(shù)據(jù)被拆分為多張表,也需要額外進(jìn)行數(shù)據(jù)操作的路由工作,因此增加了系統(tǒng)復(fù)雜度。
3.5 小結(jié)
- 垂直分表:可以把一個(gè)寬表的字段按訪問(wèn)頻次、業(yè)務(wù)耦合松緊、是否是大字段的原則拆分為多個(gè)表,這樣既能使業(yè)務(wù)清晰,還能提升部分性能。拆分后,盡量從業(yè)務(wù)角度避免聯(lián)查,否則性能方面將得不償失。
- 垂直分庫(kù):可以把多個(gè)表按業(yè)務(wù)耦合松緊歸類,分別存放在不同的庫(kù),這些庫(kù)可以分布在不同服務(wù)器,從而使訪問(wèn)壓力被多服務(wù)器負(fù)載,大大提升性能,同時(shí)能提高整體架構(gòu)的業(yè)務(wù)清晰度,不同的業(yè)務(wù)庫(kù)可根據(jù)自身情況定制優(yōu)化方案。但是它需要解決跨庫(kù)帶來(lái)的所有復(fù)雜問(wèn)題。
- 水平分庫(kù):可以把一個(gè)表的數(shù)據(jù)(按數(shù)據(jù)行)分到多個(gè)不同的庫(kù),每個(gè)庫(kù)只有這個(gè)表的部分?jǐn)?shù)據(jù),這些庫(kù)可以分布在不同服務(wù)器,從而使訪問(wèn)壓力被多服務(wù)器負(fù)載,大大提升性能。它不僅需要解決跨庫(kù)帶來(lái)的所有復(fù)雜問(wèn)題,還要解決數(shù)據(jù)路由的問(wèn)題。
- 水平分表:可以把一個(gè)表的數(shù)據(jù)(按數(shù)據(jù)行)分到多個(gè)同一個(gè)數(shù)據(jù)庫(kù)的多張表中,每個(gè)表只有這個(gè)表的部分?jǐn)?shù)據(jù),這樣做能小幅提升性能,它僅僅作為水平分庫(kù)的一個(gè)補(bǔ)充優(yōu)化。
一般來(lái)說(shuō),在系統(tǒng)設(shè)計(jì)階段就應(yīng)該根據(jù)業(yè)務(wù)耦合松緊來(lái)確定垂直分庫(kù),垂直分表方案,在數(shù)據(jù)量及訪問(wèn)壓力不是特別大的情況,首先考慮緩存、讀寫分離、索引技術(shù)等方案。若數(shù)據(jù)量極大,且持續(xù)增長(zhǎng),再考慮水平分庫(kù)分表方案。
4 .分庫(kù)分表帶來(lái)的問(wèn)題
分庫(kù)分表有效的緩解了大數(shù)據(jù)、高并發(fā)帶來(lái)的性能和壓力,也能突破網(wǎng)絡(luò)IO、硬件資源、連接數(shù)的瓶頸,但同時(shí)也帶來(lái)了一些問(wèn)題。
4.1 事務(wù)一致性問(wèn)題
由于分庫(kù)分表把數(shù)據(jù)分布在不同庫(kù)甚至不同服務(wù)器,不可避免會(huì)帶來(lái)**分布式事務(wù)**問(wèn)題,我們需要額外編程解決該問(wèn)題。
4.2 跨節(jié)點(diǎn)join
在沒(méi)有進(jìn)行分庫(kù)分表前,我們檢索商品時(shí)可以通過(guò)以下SQL對(duì)店鋪信息進(jìn)行關(guān)聯(lián)查詢:
SELECT p.*,s.[店鋪名稱],s.[信譽(yù)] FROM [商品信息] p LEFT JOIN [店鋪信息] s ON p.id = s.[所屬店鋪] WHERE...ORDER BY...LIMIT... ```
但經(jīng)過(guò)分庫(kù)分表后,[商品信息]和[店鋪信息]不在一個(gè)數(shù)據(jù)庫(kù)或一個(gè)表中,甚至不在一臺(tái)服務(wù)器上,無(wú)法通過(guò)sql語(yǔ)句進(jìn)行關(guān)聯(lián)查詢,我們需要額外編程解決該問(wèn)題。
4.3 跨節(jié)點(diǎn)分頁(yè)、排序和聚合函數(shù)
跨節(jié)點(diǎn)多庫(kù)進(jìn)行查詢時(shí),limit分頁(yè)、order by排序以及聚合函數(shù)等問(wèn)題,就變得比較復(fù)雜了。需要先在不同的分片節(jié)點(diǎn)中將數(shù)據(jù)進(jìn)行排序并返回,然后將不同分片返回的結(jié)果集進(jìn)行匯總和再次排序。例如,進(jìn)行水平分庫(kù)后的商品庫(kù),按ID倒序排序分頁(yè),取第一頁(yè):
以上流程是取第一頁(yè)的數(shù)據(jù),性能影響不大,但由于商品信息的分布在各數(shù)據(jù)庫(kù)的數(shù)據(jù)可能是隨機(jī)的,如果是取第N頁(yè),需要將所有節(jié)點(diǎn)前N頁(yè)數(shù)據(jù)都取出來(lái)合并,再進(jìn)行整體的排序,操作效率可想而知,所以請(qǐng)求頁(yè)數(shù)越大,系統(tǒng)的性能也會(huì)越差。
在使用Max、Min、Sum、Count之類的函數(shù)進(jìn)行計(jì)算的時(shí)候,與排序分頁(yè)同理,也需要先在每個(gè)分片上執(zhí)行相應(yīng)的函數(shù),然后將各個(gè)分片的結(jié)果集進(jìn)行匯總和再次計(jì)算,最終將結(jié)果返回。
4.4 主鍵避重
在分庫(kù)分表環(huán)境中,由于表中數(shù)據(jù)同時(shí)存在不同數(shù)據(jù)庫(kù)中,主鍵值平時(shí)使用的自增長(zhǎng)將無(wú)用武之地,某個(gè)分區(qū)數(shù)據(jù)庫(kù)生成的ID無(wú)法保證全局唯一。因此需要單獨(dú)設(shè)計(jì)全局主鍵,以避免跨庫(kù)主鍵重復(fù)問(wèn)題。
由于分庫(kù)分表之后,數(shù)據(jù)被分散在不同的服務(wù)器、數(shù)據(jù)庫(kù)和表中。因此,對(duì)數(shù)據(jù)的操作也就無(wú)法通過(guò)常規(guī)方式完成,并且它還帶來(lái)了一系列的問(wèn)題。我們?cè)陂_(kāi)發(fā)過(guò)程中需要通過(guò)一些中間件解決這些問(wèn)題,市面上有很多中間件可供我們選擇,其中Sharding-JDBC和mycat較為流行。
5 .總結(jié)
通過(guò)以上學(xué)習(xí)呢,我們知道,當(dāng)數(shù)據(jù)庫(kù)特別大的情況下,數(shù)據(jù)庫(kù)存儲(chǔ)數(shù)量達(dá)到了一定的閾值以后會(huì)變慢,我們需要使用分庫(kù)分表的方案來(lái)解決這個(gè)問(wèn)題。我們主要學(xué)習(xí)了垂直分表、垂直分庫(kù)、水平分表、水平分庫(kù)四種方案。當(dāng)我們選擇分庫(kù)分表以后也會(huì)帶來(lái)一些問(wèn)題,比如事務(wù)一致的問(wèn)題、垮節(jié)點(diǎn)聚合的問(wèn)題、分頁(yè)、主鍵避重等等問(wèn)題,大家也要掌握這些問(wèn)題的解決方案。