一文快速入门分库分表(必修课)
之前有不少剛?cè)肟?Java?的粉絲留言,想系統(tǒng)的學(xué)習(xí)一下分庫(kù)分表相關(guān)技術(shù),可我一直沒(méi)下定決心搞,眼下趕上公司項(xiàng)目在使用?sharding-jdbc?對(duì)現(xiàn)有?MySQL?架構(gòu)做分庫(kù)分表的改造,所以借此機(jī)會(huì)出一系分庫(kù)分表落地實(shí)踐的文章,也算是自己對(duì)架構(gòu)學(xué)習(xí)的一個(gè)總結(jié)。
我在網(wǎng)上陸陸續(xù)續(xù)的也看了一些有關(guān)于分庫(kù)分表的文章,可發(fā)現(xiàn)網(wǎng)上同質(zhì)化的資料有點(diǎn)多,而且知識(shí)點(diǎn)又都比較零碎,還沒(méi)有詳細(xì)的實(shí)戰(zhàn)案例。為了更深入的學(xué)習(xí)下,我在某些平臺(tái)買(mǎi)了點(diǎn)付費(fèi)課程,看了幾節(jié)課發(fā)現(xiàn)有點(diǎn)經(jīng)驗(yàn)的人看還可以,但對(duì)于新手入門(mén)來(lái)說(shuō),其實(shí)學(xué)習(xí)難度還是蠻大的。
為了讓新手也能看得懂,有些知識(shí)點(diǎn)我可能會(huì)用更多的篇幅加以描述,希望大家不要嫌我啰嗦,等這分庫(kù)分表系列文章完結(jié)后,我會(huì)把它做成?PDF?文檔開(kāi)源出去,能幫一個(gè)算一個(gè)吧!如果發(fā)現(xiàn)文中有哪些錯(cuò)誤或不嚴(yán)謹(jǐn)之處,歡迎大家交流指正。
具體實(shí)踐分庫(kù)分表之前在啰嗦幾句,回頭復(fù)習(xí)下分庫(kù)分表的基礎(chǔ)概念。
什么是分庫(kù)分表
其實(shí)?分庫(kù)?和?分表?是兩個(gè)概念,只不過(guò)通常分庫(kù)與分表的操作會(huì)同時(shí)進(jìn)行,以至于我們習(xí)慣性的將它們合在一起叫做分庫(kù)分表。
分庫(kù)分表是為了解決由于庫(kù)、表數(shù)據(jù)量過(guò)大,而導(dǎo)致數(shù)據(jù)庫(kù)性能持續(xù)下降的問(wèn)題。按照一定的規(guī)則,將原本數(shù)據(jù)量大的數(shù)據(jù)庫(kù)拆分成多個(gè)單獨(dú)的數(shù)據(jù)庫(kù),將原本數(shù)據(jù)量大的表拆分成若干個(gè)數(shù)據(jù)表,使得單一的庫(kù)、表性能達(dá)到最優(yōu)的效果(響應(yīng)速度快),以此提升整體數(shù)據(jù)庫(kù)性能。
如何分庫(kù)分表
分庫(kù)分表的核心理念就是對(duì)數(shù)據(jù)進(jìn)行切分(Sharding),以及切分后如何對(duì)數(shù)據(jù)的快速定位與查詢(xún)結(jié)果整合。而分庫(kù)與分表都可以從:垂直(縱向)和?水平(橫向)兩種緯度進(jìn)行切分。
下邊我們就以訂單相關(guān)的業(yè)務(wù)舉例,看看如何做庫(kù)、表的?垂直?和?水平?切分。
垂直切分
垂直切分有?垂直?分庫(kù) 和?垂直分表。
1、垂直分庫(kù)
垂直分庫(kù)相對(duì)來(lái)說(shuō)是比較好理解的,核心理念就四個(gè)字:專(zhuān)庫(kù)專(zhuān)用。
按業(yè)務(wù)類(lèi)型對(duì)表進(jìn)行分類(lèi),像訂單、支付、優(yōu)惠券、積分等相應(yīng)的表放在對(duì)應(yīng)的數(shù)據(jù)庫(kù)中。開(kāi)發(fā)者不可以跨庫(kù)直連別的業(yè)務(wù)數(shù)據(jù)庫(kù),想要其他業(yè)務(wù)數(shù)據(jù),對(duì)應(yīng)業(yè)務(wù)方可以提供?API?接口,這就是微服務(wù)的初始形態(tài)。
垂直分庫(kù)很大程度上取決于業(yè)務(wù)的劃分,但有時(shí)候業(yè)務(wù)間的劃分并不是那么清晰,比如:訂單數(shù)據(jù)的拆分要考慮到與其他業(yè)務(wù)間的關(guān)聯(lián)關(guān)系,并不是說(shuō)直接把訂單相關(guān)的表放在一個(gè)庫(kù)里這么簡(jiǎn)單。
在一定程度上,垂直分庫(kù)似乎提升了一些數(shù)據(jù)庫(kù)性能,可實(shí)際上并沒(méi)有解決由于單表數(shù)據(jù)量過(guò)大導(dǎo)致的性能問(wèn)題,所以就需要配合水平切分方式來(lái)解決。
2、垂直分表
垂直分表是基于數(shù)據(jù)表的列(字段)為依據(jù)切分的,是一種大表拆小表的模式。
例如:一張?order?訂單表,將訂單金額、訂單編號(hào)等訪問(wèn)頻繁的字段,單獨(dú)拆成一張表,把?blob?類(lèi)型這樣的大字段或訪問(wèn)不頻繁的字段,拆分出來(lái)創(chuàng)建一個(gè)單獨(dú)的擴(kuò)展表?work_extend?,這樣每張表只存儲(chǔ)原表的一部分字段,再將拆分出來(lái)的表分散到不同的庫(kù)中。
我們知道數(shù)據(jù)庫(kù)是以行為單位將數(shù)據(jù)加載到內(nèi)存中,這樣拆分以后核心表大多是訪問(wèn)頻率較高的字段,而且字段長(zhǎng)度也都較短,因而可以加載更多數(shù)據(jù)到內(nèi)存中,來(lái)增加查詢(xún)的命中率,減少磁盤(pán)IO,以此來(lái)提升數(shù)據(jù)庫(kù)性能。
垂直切分的優(yōu)點(diǎn):
- 業(yè)務(wù)間數(shù)據(jù)解耦,不同業(yè)務(wù)的數(shù)據(jù)進(jìn)行獨(dú)立的維護(hù)、監(jiān)控、擴(kuò)展。
- 在高并發(fā)場(chǎng)景下,一定程度上緩解了數(shù)據(jù)庫(kù)的壓力。
垂直切分的缺點(diǎn):
- 提升了開(kāi)發(fā)的復(fù)雜度,由于業(yè)務(wù)的隔離性,很多表無(wú)法直接訪問(wèn),必須通過(guò)接口方式聚合數(shù)據(jù)。
- 分布式事務(wù)管理難度增加。
- 數(shù)據(jù)庫(kù)還是存在單表數(shù)據(jù)量過(guò)大的問(wèn)題,并未根本上解決,需要配合水平切分。
水平切分
前邊說(shuō)了垂直切分還是會(huì)存在單庫(kù)、表數(shù)據(jù)量過(guò)大的問(wèn)題,當(dāng)我們的應(yīng)用已經(jīng)無(wú)法在細(xì)粒度的垂直切分時(shí),
依舊存在單庫(kù)讀寫(xiě)、存儲(chǔ)性能瓶頸,這時(shí)就要配合水平切分一起了,水平切分能大幅提升數(shù)據(jù)庫(kù)性能。
1、水平分庫(kù)
水平分庫(kù)是把同一個(gè)表按一定規(guī)則拆分到不同的數(shù)據(jù)庫(kù)中,每個(gè)庫(kù)可以位于不同的服務(wù)器上,以此實(shí)現(xiàn)水平擴(kuò)展,是一種常見(jiàn)的提升數(shù)據(jù)庫(kù)性能的方式。
這種方案往往能解決單庫(kù)存儲(chǔ)量及性能瓶頸問(wèn)題,但由于同一個(gè)表被分配在不同的數(shù)據(jù)庫(kù)中,數(shù)據(jù)的訪問(wèn)需要額外的路由工作,因此系統(tǒng)的復(fù)雜度也被提升了。
例如下圖,訂單DB_1、訂單DB_1、訂單DB_3?三個(gè)數(shù)據(jù)庫(kù)內(nèi)有完全相同的表?order,我們?cè)谠L問(wèn)某一筆訂單時(shí)可以通過(guò)對(duì)訂單的訂單編號(hào)取模的方式?訂單編號(hào) mod 3 (數(shù)據(jù)庫(kù)實(shí)例數(shù))?,指定該訂單應(yīng)該在哪個(gè)數(shù)據(jù)庫(kù)中操作。
2、水平分表
水平分表是在同一個(gè)數(shù)據(jù)庫(kù)內(nèi),把一張大數(shù)據(jù)量的表按一定規(guī)則,切分成多個(gè)結(jié)構(gòu)完全相同表,而每個(gè)表只存原表的一部分?jǐn)?shù)據(jù)。
例如:一張?order?訂單表有 900萬(wàn)數(shù)據(jù),經(jīng)過(guò)水平拆分出來(lái)三個(gè)表,order_1、order_2、order_3,每張表存有數(shù)據(jù) 300萬(wàn),以此類(lèi)推。
水平分表盡管拆分了表,但子表都還是在同一個(gè)數(shù)據(jù)庫(kù)實(shí)例中,只是解決了單一表數(shù)據(jù)量過(guò)大的問(wèn)題,并沒(méi)有將拆分后的表分散到不同的機(jī)器上,還在競(jìng)爭(zhēng)同一個(gè)物理機(jī)的CPU、內(nèi)存、網(wǎng)絡(luò)IO等。要想進(jìn)一步提升性能,就需要將拆分后的表分散到不同的數(shù)據(jù)庫(kù)中,達(dá)到分布式的效果。
水平切分的優(yōu)點(diǎn):
- 解決高并發(fā)時(shí)單庫(kù)數(shù)據(jù)量過(guò)大的問(wèn)題,提升系統(tǒng)穩(wěn)定性和負(fù)載能力。
- 業(yè)務(wù)系統(tǒng)改造的工作量不是很大。
水平切分的缺點(diǎn):
- 跨分片的事務(wù)一致性難以保證。
- 跨庫(kù)的join關(guān)聯(lián)查詢(xún)性能較差。
- 擴(kuò)容的難度和維護(hù)量較大,(拆分成幾千張子表想想都恐怖)。
一定規(guī)則是什么
我們上邊提到過(guò)很多次?一定規(guī)則?,這個(gè)規(guī)則其實(shí)是一種路由算法,就是決定一條數(shù)據(jù)具體應(yīng)該存在哪個(gè)數(shù)據(jù)庫(kù)的哪張表里。
常見(jiàn)的有?取模算法?和?范圍限定算法
1、取模算法
按字段取模(對(duì)hash結(jié)果取余數(shù) (hash() mod N),N為數(shù)據(jù)庫(kù)實(shí)例數(shù)或子表數(shù)量)是最為常見(jiàn)的一種切分方式。
還拿?order?訂單表舉例,先對(duì)數(shù)據(jù)庫(kù)從 0 到 N-1進(jìn)行編號(hào),對(duì)?order?訂單表中?work_no?訂單編號(hào)字段進(jìn)行取模,得到余數(shù)?i,i=0存第一個(gè)庫(kù),i=1存第二個(gè)庫(kù),i=2存第三個(gè)庫(kù)....以此類(lèi)推。
這樣同一筆訂單的數(shù)據(jù)都會(huì)存在同一個(gè)庫(kù)、表里,查詢(xún)時(shí)用相同的規(guī)則,用?work_no?訂單編號(hào)作為查詢(xún)條件,就能快速的定位到數(shù)據(jù)。
優(yōu)點(diǎn):
- 數(shù)據(jù)分片相對(duì)比較均勻,不易出現(xiàn)請(qǐng)求都打到一個(gè)庫(kù)上的情況。
缺點(diǎn):
- 這種算法存在一些問(wèn)題,當(dāng)某一臺(tái)機(jī)器宕機(jī),本應(yīng)該落在該數(shù)據(jù)庫(kù)的請(qǐng)求就無(wú)法得到正確的處理,這時(shí)宕掉的實(shí)例會(huì)被踢出集群,此時(shí)算法變成hash(userId) mod N-1,用戶(hù)信息可能就不再在同一個(gè)庫(kù)中了。
2、范圍限定算法
按照?時(shí)間區(qū)間?或?ID區(qū)間?來(lái)切分,比如:我們切分的是用戶(hù)表,可以定義每個(gè)庫(kù)的?User?表里只存10000條數(shù)據(jù),第一個(gè)庫(kù)只存?userId?從1 ~ 9999的數(shù)據(jù),第二個(gè)庫(kù)存?userId?為10000 ~ 20000,第三個(gè)庫(kù)存?userId?為 20001~ 30000......以此類(lèi)推,按時(shí)間范圍也是同理。
優(yōu)點(diǎn):
- 單表數(shù)據(jù)量是可控的
- 水平擴(kuò)展簡(jiǎn)單只需增加節(jié)點(diǎn)即可,無(wú)需對(duì)其他分片的數(shù)據(jù)進(jìn)行遷移
- 能快速定位要查詢(xún)的數(shù)據(jù)在哪個(gè)庫(kù)
缺點(diǎn):
- 由于連續(xù)分片可能存在數(shù)據(jù)熱點(diǎn),比如按時(shí)間字段分片,可能某一段時(shí)間內(nèi)訂單驟增,可能會(huì)被頻繁的讀寫(xiě),而有些分片存儲(chǔ)的歷史數(shù)據(jù),則很少被查詢(xún)。
分庫(kù)分表的難點(diǎn)
1、分布式事務(wù)
由于表分布在不同庫(kù)中,不可避免會(huì)帶來(lái)跨庫(kù)事務(wù)問(wèn)題。一般可使用 "三階段提交?"和 "兩階段提交" 處理,但是這種方式性能較差,代碼開(kāi)發(fā)量也比較大。通常做法是做到最終一致性的方案,如果不苛求系統(tǒng)的實(shí)時(shí)一致性,只要在允許的時(shí)間段內(nèi)達(dá)到最終一致性即可,采用事務(wù)補(bǔ)償?shù)姆绞健?/p>
這里我應(yīng)用阿里的分布式事務(wù)框架Seata?來(lái)做分布式事務(wù)的管理,后邊會(huì)結(jié)合實(shí)際案例。
2、分頁(yè)、排序、跨庫(kù)聯(lián)合查詢(xún)
分頁(yè)、排序、聯(lián)合查詢(xún)是開(kāi)發(fā)中使用頻率非常高的功能,但在分庫(kù)分表后,這些看似普通的操作卻是讓人非常頭疼的問(wèn)題。將分散在不同庫(kù)中表的數(shù)據(jù)查詢(xún)出來(lái),再將所有結(jié)果進(jìn)行匯總整理后提供給用戶(hù)。
3、分布式主鍵
分庫(kù)分表后數(shù)據(jù)庫(kù)的自增主鍵意義就不大了,因?yàn)槲覀儾荒芤揽繂蝹€(gè)數(shù)據(jù)庫(kù)實(shí)例上的自增主鍵來(lái)實(shí)現(xiàn)不同數(shù)據(jù)庫(kù)之間的全局唯一主鍵,此時(shí)一個(gè)能夠生成全局唯一ID的系統(tǒng)是非常必要的,那么這個(gè)全局唯一ID就叫?分布式ID。
4、讀寫(xiě)分離
不難發(fā)現(xiàn)大部分主流的關(guān)系型數(shù)據(jù)庫(kù)都提供了主從架構(gòu)的高可用方案,而我們需要實(shí)現(xiàn)?讀寫(xiě)分離?+?分庫(kù)分表,讀庫(kù)與寫(xiě)庫(kù)都要做分庫(kù)分表處理,后邊會(huì)有具體實(shí)戰(zhàn)案例。
5、數(shù)據(jù)脫敏
數(shù)據(jù)脫敏,是指對(duì)某些敏感信息通過(guò)脫敏規(guī)則進(jìn)行數(shù)據(jù)轉(zhuǎn)換,從而實(shí)現(xiàn)敏感隱私數(shù)據(jù)的可靠保護(hù),如身份證號(hào)、手機(jī)號(hào)、卡號(hào)、賬號(hào)密碼等個(gè)人信息,一般這些都需要進(jìn)行做脫敏處理。
分庫(kù)分表工具
我還是那句話(huà),盡量不要自己造輪子,因?yàn)樽约涸斓妮喿涌赡懿荒敲磮A,業(yè)界已經(jīng)有了很多比較成熟的分庫(kù)分表中間件,我們根據(jù)自身的業(yè)務(wù)需求挑選,將更多的精力放在業(yè)務(wù)實(shí)現(xiàn)上。
- sharding-jdbc(當(dāng)當(dāng))
- TSharding(蘑菇街)
- Atlas(奇虎360)
- Cobar(阿里巴巴)
- MyCAT(基于Cobar)
- Oceanus(58同城)
- Vitess(谷歌)
為什么選 sharding-jdbc
sharding-jdbc?是一款輕量級(jí)?Java?框架,以?jar?包形式提供服務(wù),是屬于客戶(hù)端產(chǎn)品不需要額外部署,它相當(dāng)于是個(gè)增強(qiáng)版的?JDBC?驅(qū)動(dòng);相比之下像?Mycat?這類(lèi)需要單獨(dú)的部署服務(wù)的服務(wù)端產(chǎn)品,就稍顯復(fù)雜了。況且我想把更多精力放在實(shí)現(xiàn)業(yè)務(wù),不想做額外的運(yùn)維工作。
- sharding-jdbc的兼容性也非常強(qiáng)大,適用于任何基于?JDBC?的?ORM?框架,如:JPA,?Hibernate,Mybatis,Spring JDBC Template?或直接使用的?JDBC。
- 完美兼容任何第三方的數(shù)據(jù)庫(kù)連接池,如:DBCP,?C3P0,?BoneCP,Druid,?HikariCP?等,幾乎對(duì)所有關(guān)系型數(shù)據(jù)庫(kù)都支持。
不難發(fā)現(xiàn)確實(shí)是比較強(qiáng)大的一款工具,而且它對(duì)項(xiàng)目的侵入性很小,幾乎不用做任何代碼層的修改,也無(wú)需修改?SQL?語(yǔ)句,只需配置待分庫(kù)分表的數(shù)據(jù)表即可。
總結(jié)
簡(jiǎn)單的回顧一下分庫(kù)分表的基礎(chǔ)知識(shí),接下來(lái)的文章會(huì)配合實(shí)戰(zhàn)項(xiàng)目介紹?sharding-jdbc?在分庫(kù)分表中的各個(gè)功能點(diǎn)。
?
原文鏈接
本文為阿里云原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載。
總結(jié)
以上是生活随笔為你收集整理的一文快速入门分库分表(必修课)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 基于 Flink 的典型 ETL 场景实
- 下一篇: 如何用好云的弹性