分布式数据库选型——数据水平拆分方案
概述
水平拆分的概念隨著分布式數(shù)據(jù)庫(kù)的推廣已為大部分人熟知。分庫(kù)分表、異構(gòu)索引、小表廣播、這些功能幾乎是產(chǎn)品功能需求標(biāo)配。然而有些客戶(hù)使用分布式數(shù)據(jù)庫(kù)后的體驗(yàn)不盡如意。
本文嘗試從數(shù)據(jù)的角度總結(jié)分布式數(shù)據(jù)的復(fù)制(replication)和分區(qū)(partition)技術(shù)原理和方案,其中分區(qū)也有稱(chēng)為分片(sharding),希望能引起讀者一些思考,在分布式數(shù)據(jù)庫(kù)選型中能注意這些細(xì)節(jié)的區(qū)別,選擇適合業(yè)務(wù)的數(shù)據(jù)水平拆分方案。
分布式數(shù)據(jù)庫(kù)架構(gòu)
分布式數(shù)據(jù)庫(kù)以集群形式存在,有多個(gè)節(jié)點(diǎn)。集群架構(gòu)有共享磁盤(pán)架構(gòu)(shared-disk)和無(wú)共享架構(gòu)(shared-nothing)。后者有時(shí)也稱(chēng)為水平擴(kuò)展(horizontal scale)或向外擴(kuò)展(scale out),本文主要總結(jié)無(wú)共享架構(gòu)方案。
無(wú)共享架構(gòu)的各個(gè)節(jié)點(diǎn)之間的通信都是軟件層面使用網(wǎng)絡(luò)實(shí)現(xiàn),不同產(chǎn)品在架構(gòu)不同導(dǎo)致這個(gè)細(xì)節(jié)也不同。有些架構(gòu)是計(jì)算與存儲(chǔ)分離。計(jì)算節(jié)點(diǎn)特點(diǎn)是無(wú)狀態(tài)(即數(shù)據(jù)不要求持久化),通過(guò)集群方式管理,可以水平擴(kuò)展;存儲(chǔ)節(jié)點(diǎn)有數(shù)據(jù),使用復(fù)制和分區(qū)技術(shù),節(jié)點(diǎn)間任務(wù)集中調(diào)度或者獨(dú)立交互。了解這個(gè)架構(gòu)細(xì)節(jié)都可用性分析會(huì)更加具體。具體分布式數(shù)據(jù)庫(kù)架構(gòu)有哪些請(qǐng)參考《一些關(guān)系數(shù)據(jù)庫(kù)的架構(gòu)總結(jié)》。
這里節(jié)點(diǎn)的實(shí)際體現(xiàn)形式可以是一個(gè)機(jī)器,也可以是機(jī)器上的一個(gè)實(shí)例。比如說(shuō)有些數(shù)據(jù)庫(kù)支持單機(jī)安裝多個(gè)實(shí)例,如MySQL。每個(gè)節(jié)點(diǎn)具備一定的資源和能力。資源指的是CPU、內(nèi)存和磁盤(pán),能力是提供數(shù)據(jù)讀寫(xiě)和存儲(chǔ)能力。分布式數(shù)據(jù)庫(kù)需要把多個(gè)節(jié)點(diǎn)的能力聚集到一起集中管理,只是不同分布式數(shù)據(jù)庫(kù)產(chǎn)品對(duì)資源的管理能力各有特點(diǎn)。
在分布式數(shù)據(jù)庫(kù)里,數(shù)據(jù)隨處可見(jiàn),這是最容易讓人混淆的地方。因?yàn)閿?shù)據(jù)經(jīng)過(guò)復(fù)制和分區(qū)后會(huì)有兩種存在形式:副本(replica)和分區(qū)(partition)。
數(shù)據(jù)的復(fù)制(replication)
復(fù)制(replication)指在幾個(gè)不同的節(jié)點(diǎn)上保存數(shù)據(jù)的相同副本(replica)。復(fù)制提供了冗余的能力。其作用一是提供高可用能力:如果一個(gè)節(jié)點(diǎn)不可用,剩余的節(jié)點(diǎn)可以快速提供數(shù)據(jù)服務(wù)。作用二是提供讀寫(xiě)分離能力。常見(jiàn)的有兩副本和三副本架構(gòu)。
多個(gè)副本內(nèi)容相同,角色會(huì)有區(qū)分。常見(jiàn)的是一個(gè)副本是Leader角色(有的也稱(chēng)主副本),默認(rèn)提供讀寫(xiě)服務(wù);其他副本是Follower角色(有的也稱(chēng)備副本),默認(rèn)不提供服務(wù)。這種架構(gòu)也稱(chēng)為基于單Leader的(Single Leader-based)。還有其他架構(gòu)是多Leader的,每個(gè)Leader都有數(shù)據(jù)要復(fù)制到其他Leader或Follower,這種架構(gòu)會(huì)有個(gè)明顯的問(wèn)題就是數(shù)據(jù)沖突處理。如果產(chǎn)品層面不處理,用戶(hù)直接使用風(fēng)險(xiǎn)會(huì)很高。
后面討論的是前者:基于單Leader副本架構(gòu)。
多副本之間數(shù)據(jù)同步不是依賴(lài)業(yè)務(wù)多寫(xiě),而是采用副本間復(fù)制事務(wù)日志(Redo)技術(shù)。復(fù)制的方式有同步復(fù)制和異步復(fù)制。使用同步復(fù)制方式,備副本要收到Redo并落盤(pán)主副本才能提交,也叫強(qiáng)同步;使用異步復(fù)制方式,Follower副本相對(duì)Leader副本內(nèi)容會(huì)有延時(shí),具體延時(shí)多少取決于Leader副本上事務(wù)量、網(wǎng)絡(luò)傳輸速度、Follower副本所在節(jié)點(diǎn)的負(fù)載和能力。強(qiáng)同步的缺點(diǎn)時(shí)主副本寫(xiě)性能會(huì)下降,同時(shí)如果備副本不可用主副本也不能提供服務(wù)(變相的解決方案是復(fù)制方式降級(jí)為異步復(fù)制)。
傳統(tǒng)關(guān)系型數(shù)據(jù)庫(kù)還有一種用法一主兩備架構(gòu),使用同步復(fù)制,只要任何一個(gè)備副本收到Redo,主副本的事務(wù)就可以提交。這個(gè)方案優(yōu)點(diǎn)是保障了數(shù)據(jù)在多個(gè)副本中存在,高可用時(shí)有候選副本,也不用擔(dān)心掛掉一個(gè)備副本會(huì)影響主副本。它的缺點(diǎn)是不能自動(dòng)知道哪個(gè)候選副本擁有主副本最新最全的數(shù)據(jù),也不強(qiáng)制要求兩個(gè)備副本都要擁有全部數(shù)據(jù)。
還有一類(lèi)三副本架構(gòu)在復(fù)制時(shí)使用的是Paxos協(xié)議,三副本會(huì)就Redo落盤(pán)事件進(jìn)行投票,有兩個(gè)副本成功了Leader副本的事務(wù)即可提交。這個(gè)表面上跟上面?zhèn)鹘y(tǒng)一主兩備的三副本效果一樣,實(shí)際上還是有區(qū)別的。區(qū)別一是使用Paxos協(xié)議時(shí),如果Leader副本自身投票慢了,兩個(gè)Follower副本投票成功,Leader副本的事務(wù)也是能提交的;區(qū)別二是第三個(gè)副本最終也必須寫(xiě)Redo成功,否則其狀態(tài)就是異常,產(chǎn)品自身可以發(fā)現(xiàn)并自動(dòng)修復(fù)(如重新創(chuàng)建一個(gè)副本);區(qū)別三是使用Paxos協(xié)議時(shí),在Leader副本不可用時(shí)還可以自動(dòng)選出新的Leader副本并且擁有老Leader副本的最新數(shù)據(jù)。這里其實(shí)說(shuō)的是高可用機(jī)制。同樣,這里對(duì)用戶(hù)而言也不知道哪個(gè)Follower副本擁有最新最全的數(shù)據(jù),如果訪問(wèn)Follower副本(讀寫(xiě)分離),也可能發(fā)現(xiàn)數(shù)據(jù)有延時(shí)。
大部分?jǐn)?shù)據(jù)庫(kù)做副本復(fù)制使用的是Redo,也稱(chēng)為物理同步。在應(yīng)用Redo的時(shí)候直接是數(shù)據(jù)塊變更。使用物理同步機(jī)制的備副本是不提供寫(xiě)服務(wù),不能修改。還有一類(lèi)復(fù)制使用的是Binlog,也稱(chēng)為邏輯同步。Binlog里只包含已提交的事務(wù),并且在應(yīng)用的時(shí)候是通過(guò)執(zhí)行SQL。使用邏輯同步的備副本通常也可能是主副本,可以修改(如MySQL的雙向復(fù)制架構(gòu)Master-Master)。如果目標(biāo)端數(shù)據(jù)不對(duì),應(yīng)用SQL會(huì)失敗,這個(gè)復(fù)制就會(huì)中斷需要人介入處理。這也進(jìn)一步加深了主備副本不一致的概率。
關(guān)于副本角色的粒度,有多種實(shí)現(xiàn)方案。
傳統(tǒng)關(guān)系數(shù)據(jù)庫(kù)主備架構(gòu),主副本或備副本的粒度就是實(shí)例。對(duì)于主實(shí)例(Primary)而言,里面所有數(shù)據(jù)庫(kù)(或SCHEMA)的所有表的角色都是主;備實(shí)例(Standby)里數(shù)據(jù)則都是備副本。如果發(fā)生高可用切換,業(yè)務(wù)會(huì)中斷幾十秒或幾分鐘然后恢復(fù)(需要人工處理或自動(dòng)化腳本處理)。
還有一種粒度是到表。即一個(gè)節(jié)點(diǎn)內(nèi)有些表是Leader副本,有些表是Follower副本,這樣這個(gè)節(jié)點(diǎn)就不能簡(jiǎn)單的說(shuō)是主節(jié)點(diǎn)(實(shí)例)或備節(jié)點(diǎn)(實(shí)例)。這個(gè)副本角色細(xì)節(jié)業(yè)務(wù)也是可以獲取的,如果發(fā)生高可用切換,業(yè)務(wù)會(huì)中斷十幾秒然后恢復(fù)。
還有一種粒度是存儲(chǔ)級(jí)別的定長(zhǎng)塊。即一個(gè)節(jié)點(diǎn)的存儲(chǔ)里,部分?jǐn)?shù)據(jù)塊是Leader副本,部分?jǐn)?shù)據(jù)塊是Follower副本。這種對(duì)業(yè)務(wù)就完全透明,業(yè)務(wù)基本不感知高可用切換。
數(shù)據(jù)的分區(qū)(partition)
上面總結(jié)的是數(shù)據(jù)的復(fù)制(冗余,多副本),對(duì)于非常大的數(shù)據(jù)集(表)或者非常高的訪問(wèn)量(QPS),僅僅靠復(fù)制是不夠的,還需要對(duì)數(shù)據(jù)進(jìn)行分區(qū)(partition),也稱(chēng)為分片(sharding)。
分區(qū)粒度
首先這里的分區(qū)(partition)是一種抽象概念,在不同數(shù)據(jù)庫(kù)產(chǎn)品里這個(gè)體現(xiàn)是不一樣的。如在MongoDB, Elasticsearch中體現(xiàn)為分片(shard),在HBase中體現(xiàn)為區(qū)域塊(Region),Bigtable中體現(xiàn)為表塊(tablet),ORACLE中體現(xiàn)為分區(qū)(partition),Couchbase中體現(xiàn)為虛擬桶(vBucket)。可見(jiàn)不同的數(shù)據(jù)庫(kù)產(chǎn)品數(shù)據(jù)分區(qū)的粒度不同。在分布式關(guān)系數(shù)據(jù)庫(kù)中間件中,分片的粒度是分表(物理表);在真正的分布式關(guān)系數(shù)據(jù)庫(kù)里,分片的粒度有分區(qū)(partition,同ORACLE)或者區(qū)域塊(Region)。
分區(qū)粒度對(duì)業(yè)務(wù)研發(fā)的使用體驗(yàn)影響很大。
比如說(shuō)中間件常用分庫(kù)分表方案,使用時(shí)對(duì)開(kāi)發(fā)和運(yùn)維會(huì)有一些要求。如建很多同構(gòu)的表并后期維護(hù)、要求SQL帶上拆分鍵,還有一些功能限制(如跨庫(kù)JOIN問(wèn)題)、底層存儲(chǔ)節(jié)點(diǎn)用的數(shù)據(jù)庫(kù)自身高可用和多副本的數(shù)據(jù)一致問(wèn)題等等。不同的中間件產(chǎn)品能力上也有區(qū)別,互聯(lián)網(wǎng)大廠的產(chǎn)品由于內(nèi)部場(chǎng)景培育很久,做的相對(duì)成熟一些。
體驗(yàn)最好的分區(qū)粒度就是存儲(chǔ)級(jí)別的Region,業(yè)務(wù)研發(fā)完全不用關(guān)心分片細(xì)節(jié),也無(wú)法干預(yù)分片細(xì)節(jié)。當(dāng)有些場(chǎng)景追求性能需要干預(yù)數(shù)據(jù)分布特點(diǎn)時(shí)就不好處理。
介入這兩種策略之間的就是分區(qū)。物理上業(yè)務(wù)只要?jiǎng)?chuàng)建一個(gè)分區(qū)表,根據(jù)業(yè)務(wù)特點(diǎn)指定分區(qū)策略(包含分區(qū)列、拆分算法、分區(qū)數(shù)目等)。
數(shù)據(jù)復(fù)制是為了冗余和高可用,數(shù)據(jù)分區(qū)主要是為了可擴(kuò)展性。不管使用哪種分區(qū)方案,業(yè)務(wù)的每條數(shù)據(jù)(記錄)屬于且僅屬于一個(gè)分區(qū)(或分片sharding),同一個(gè)分區(qū)(分片)只會(huì)存在于一個(gè)節(jié)點(diǎn)。前面說(shuō)了每個(gè)節(jié)點(diǎn)代表了一定的資源和能力。當(dāng)復(fù)制和分區(qū)(分片)一起使用的時(shí)候,注意區(qū)分你看到的數(shù)據(jù)。
分區(qū)策略
分區(qū)的目標(biāo)是將大量數(shù)據(jù)和訪問(wèn)請(qǐng)求均勻分布在多個(gè)節(jié)點(diǎn)上。如果每個(gè)節(jié)點(diǎn)均勻承擔(dān)數(shù)據(jù)和請(qǐng)求,那么理論上10個(gè)節(jié)點(diǎn)就應(yīng)該能承擔(dān)10倍于單節(jié)點(diǎn)的數(shù)據(jù)量和訪問(wèn)量。這個(gè)理論是忽略了復(fù)制產(chǎn)生的Follower副本的存在。Follower副本的空間和內(nèi)存是不可能跟其他Leader副本共享的,但是計(jì)算能力(CPU)是可以的。當(dāng)所有節(jié)點(diǎn)都提供服務(wù)的時(shí)候(多活),是計(jì)算資源最大利用。
然而如果分區(qū)是不均勻的,一些分區(qū)的數(shù)據(jù)量或者請(qǐng)求量會(huì)相對(duì)比較高,出現(xiàn)數(shù)據(jù)偏斜(skew),這個(gè)可能導(dǎo)致節(jié)點(diǎn)資源利用率和負(fù)載也不均衡。偏斜集中的數(shù)據(jù)我們又稱(chēng)為熱點(diǎn)數(shù)據(jù)。避免熱點(diǎn)數(shù)據(jù)的直接方法就是數(shù)據(jù)存儲(chǔ)時(shí)隨機(jī)分配(沒(méi)有規(guī)則)給節(jié)點(diǎn),缺點(diǎn)是讀取的時(shí)候不知道去哪個(gè)分區(qū)找該記錄,只有掃描所有分區(qū)了,所以這個(gè)方法意義不大。實(shí)際常用的分區(qū)策略都是有一定的規(guī)則。
這個(gè)規(guī)則可以是業(yè)務(wù)規(guī)則,也可以不是。
業(yè)務(wù)規(guī)則的分區(qū)首先是選取一個(gè)或一組列作為分區(qū)鍵,然后選取拆分方法。比如說(shuō)根據(jù)鍵的范圍(Range)分區(qū),分區(qū)數(shù)量和邊界時(shí)確定的(后期還可以新增分區(qū))。好處時(shí)針對(duì)分區(qū)鍵的范圍掃描性能會(huì)比較好。分布式數(shù)據(jù)庫(kù)中間件的分庫(kù)分表、分區(qū)表的分區(qū)都支持RANGE 拆分函數(shù)。各個(gè)產(chǎn)品拆分細(xì)節(jié)上面會(huì)有一些創(chuàng)新。Range分區(qū)的缺點(diǎn)是某些特定的訪問(wèn)模式會(huì)導(dǎo)致熱點(diǎn)。比如說(shuō)根據(jù)時(shí)間列做RANGE分區(qū),業(yè)務(wù)寫(xiě)入和讀寫(xiě)數(shù)據(jù)集中在最近的時(shí)間,就可能導(dǎo)致各個(gè)分區(qū)負(fù)載不均衡。這只是一個(gè)缺點(diǎn),業(yè)務(wù)層面還要考慮這樣做的好處。比如說(shuō)刪除歷史分區(qū)比較快。
還有種拆分方法是散列(HASH)分區(qū),分區(qū)數(shù)量和邊界是確定的(后期可以做分區(qū)分裂)。這時(shí)各個(gè)數(shù)據(jù)的分布是否均衡就取決于各個(gè)產(chǎn)品實(shí)現(xiàn)機(jī)制。大部分做法是使用一個(gè)散列(HASH)函數(shù)對(duì)Key計(jì)算一個(gè)值,然后針?lè)侄未鎯?chǔ)。
有的產(chǎn)品會(huì)使用這個(gè)HASH值對(duì)分區(qū)數(shù)取模,這個(gè)方法可能引起分區(qū)數(shù)據(jù)分布不均勻(若MySQL的Key分區(qū))。此外如果要調(diào)整分區(qū)數(shù),則需要移動(dòng)所有數(shù)據(jù)。ORACLE的HASH分區(qū)時(shí)會(huì)先選取最接近分區(qū)數(shù)的一個(gè)2的冪值,對(duì)于分區(qū)數(shù)大于這個(gè)值的分區(qū),會(huì)從前面分區(qū)里調(diào)過(guò)來(lái)。所以O(shè)RACLE 建議HASH分區(qū)數(shù)為2的冪。M有SQL建議Key分區(qū)數(shù)為奇數(shù)時(shí)數(shù)據(jù)分布最均勻。
此外在現(xiàn)有分區(qū)下還可以再做一次分區(qū),分區(qū)鍵和分區(qū)方法都可以不一樣。通常稱(chēng)為兩級(jí)分區(qū)。比如說(shuō)分庫(kù)分表時(shí),分庫(kù)和分表策略不一樣就是兩級(jí)分區(qū);分區(qū)表也支持兩級(jí)分區(qū)。
有業(yè)務(wù)規(guī)則的分區(qū)方案的特點(diǎn)就是使用上。SQL如果要性能好建議帶上分區(qū)鍵,這樣分布式數(shù)據(jù)庫(kù)才可以直接定位到所訪問(wèn)數(shù)據(jù)所在的分片;否則,數(shù)據(jù)庫(kù)就要掃描所有分區(qū)去查詢(xún)數(shù)據(jù)。通常分區(qū)鍵只能選取一個(gè)或一組業(yè)務(wù)字段,代表的是一個(gè)業(yè)務(wù)維度,那么另外一種業(yè)務(wù)維度的SQL請(qǐng)求性能就會(huì)不好。個(gè)別分布式數(shù)據(jù)庫(kù)產(chǎn)品在HASH 方法上支持兩種維度的分區(qū)列,其前提是在業(yè)務(wù)構(gòu)造數(shù)據(jù)時(shí)讓這兩個(gè)列有著內(nèi)部一致的分區(qū)邏輯。
詳情可以參考《說(shuō)說(shuō)分庫(kù)分表的一個(gè)最佳實(shí)踐》。
另外一種分區(qū)策略就是無(wú)業(yè)務(wù)規(guī)則的,在存儲(chǔ)級(jí)別按塊的大小切分為多個(gè)定長(zhǎng)塊(Region)。這個(gè)分區(qū)對(duì)業(yè)務(wù)而言就是透明的,所以使用體驗(yàn)上會(huì)相對(duì)好一些。
不過(guò),分布式數(shù)據(jù)庫(kù)里的數(shù)據(jù)分區(qū)除了存儲(chǔ)數(shù)據(jù)還要提供讀寫(xiě)服務(wù)。業(yè)務(wù)讀寫(xiě)數(shù)據(jù)的SQL本身是帶業(yè)務(wù)邏輯的,如果一次SQL請(qǐng)求訪問(wèn)的數(shù)據(jù)分散到多個(gè)分區(qū),而這些分區(qū)又散落在不同的節(jié)點(diǎn)上,不可避免的會(huì)發(fā)生跨節(jié)點(diǎn)的請(qǐng)求。如果是多表連接,這種情形更容易出現(xiàn)。如果這個(gè)業(yè)務(wù)請(qǐng)求有事務(wù),那這就產(chǎn)生了分布式事務(wù)。分布式事務(wù)解決方案有兩種,強(qiáng)一致的兩階段提交(XA)方案和最終一致的TCC方案。詳情請(qǐng)參考《說(shuō)說(shuō)數(shù)據(jù)庫(kù)事務(wù)和開(kāi)發(fā)(下)—— 分布式事務(wù)》。
這里主要提示跨節(jié)點(diǎn)的請(qǐng)求帶來(lái)的性能衰減。當(dāng)然,硬件方面萬(wàn)兆網(wǎng)卡加RDMA技術(shù)下網(wǎng)絡(luò)延時(shí)已經(jīng)縮小很多,但是當(dāng)分布式數(shù)據(jù)庫(kù)的請(qǐng)求量(QPS)非常高時(shí),或者分布式數(shù)據(jù)庫(kù)是多機(jī)房部署(比如說(shuō)兩地三中心)時(shí),跨機(jī)房的網(wǎng)絡(luò)延時(shí)還是不可忽視,跨節(jié)點(diǎn)的請(qǐng)求帶來(lái)的性能衰減也會(huì)很明顯。所以有業(yè)務(wù)規(guī)則的分區(qū)策略可以提供策略給業(yè)務(wù)控制自己的數(shù)據(jù)分區(qū)分布特點(diǎn),非常適合做異地多活和單元化類(lèi)業(yè)務(wù)。此外還有個(gè)常用的規(guī)避跨節(jié)點(diǎn)請(qǐng)求讀的方法就是小表廣播,即將個(gè)別沒(méi)有分區(qū)的表的數(shù)據(jù)復(fù)制到其他分區(qū)所在的節(jié)點(diǎn),這樣相關(guān)業(yè)務(wù)數(shù)據(jù)分區(qū)的JOIN就是在本地節(jié)點(diǎn)內(nèi)部完成。這里就看復(fù)制使用的是物理同步還是邏輯同步,以及同步的延時(shí)是否滿(mǎn)足業(yè)務(wù)需求。
分區(qū)數(shù)量
關(guān)于分區(qū)數(shù)量也需要評(píng)估。如果是無(wú)規(guī)則的分區(qū)策略,由于每個(gè)分區(qū)(分片)是定長(zhǎng)塊,那么分區(qū)數(shù)量就由總數(shù)據(jù)大小除以定長(zhǎng)塊大小,對(duì)業(yè)務(wù)也是透明的。這里總結(jié)的是有業(yè)務(wù)規(guī)則的分區(qū)的數(shù)量。
使用分區(qū)的目的是為了擴(kuò)展性,具體就是能將不同分區(qū)分散多多個(gè)節(jié)點(diǎn)上,發(fā)揮多個(gè)節(jié)點(diǎn)的資源和能力。所以分區(qū)數(shù)一定要大于可用的資源節(jié)點(diǎn)數(shù),為了考慮到將來(lái)分布式數(shù)據(jù)庫(kù)可能會(huì)擴(kuò)容,分區(qū)數(shù)應(yīng)該是數(shù)倍于當(dāng)前規(guī)劃的節(jié)點(diǎn)數(shù)。這是一個(gè)總的指導(dǎo)思想。由于不同的分布式數(shù)據(jù)庫(kù)其節(jié)點(diǎn)的表示方法不一樣,實(shí)施的時(shí)候會(huì)略有不同。
比如說(shuō)在分布式數(shù)據(jù)庫(kù)中間件架構(gòu)里,數(shù)據(jù)存儲(chǔ)的節(jié)點(diǎn)是實(shí)例,數(shù)據(jù)分區(qū)的粒度是分表(物理表),中間還有一層分庫(kù)的維度。分布式數(shù)據(jù)庫(kù)實(shí)例:總物理實(shí)例數(shù):總物理分庫(kù)數(shù):總物理分表數(shù)=1:M:N:X 。X是分區(qū)的數(shù)量,N 是總分庫(kù)數(shù)。X 是固定的,如果要調(diào)整分區(qū)數(shù),成本非常高,所以一般都是提前規(guī)劃好。N 是總分庫(kù)數(shù),是2的冪。 M 是實(shí)例的數(shù)量,也建議是2的冪,決定了最大能用多少節(jié)點(diǎn)的資源。 N/M 的結(jié)果決定了未來(lái)能擴(kuò)容的倍數(shù)。分布式數(shù)據(jù)庫(kù)中間件由于數(shù)據(jù)分區(qū)落在具體的節(jié)點(diǎn)后就不能自由移動(dòng),其擴(kuò)容方式多是對(duì)每個(gè)實(shí)例一分為二,最好的途徑就是利用數(shù)據(jù)庫(kù)(MySQL)自身的主從復(fù)制搭建新的備實(shí)例擴(kuò)容節(jié)點(diǎn)數(shù)。
此外分區(qū)數(shù)還要考慮到單個(gè)分區(qū)的容量和請(qǐng)求量是否滿(mǎn)足需求。即分區(qū)是否到位。這個(gè)也是需要業(yè)務(wù)評(píng)估的。在使用分區(qū)表的分區(qū)方案的分布式數(shù)據(jù)庫(kù)里,分區(qū)數(shù)也是結(jié)合上面兩點(diǎn)考慮的。
當(dāng)然分區(qū)數(shù)太大了,可能會(huì)增加分布數(shù)據(jù)庫(kù)內(nèi)部管理成本。分區(qū)數(shù)量跟分區(qū)粒度恰好是相反關(guān)系,二者都需要取一個(gè)合適的值。
分區(qū)數(shù)量一旦確定后,調(diào)整的成本非常高,通常會(huì)引起數(shù)據(jù)重分布。有些產(chǎn)品可以針對(duì)特定類(lèi)型的分區(qū)做分區(qū)分裂。如RANGE分區(qū)可以分裂為兩個(gè)RANGE, HASH分區(qū)也可以一分為二。只要這個(gè)分區(qū)分裂的邏輯是數(shù)據(jù)庫(kù)內(nèi)部邏輯實(shí)現(xiàn),保證數(shù)據(jù)不丟,且對(duì)業(yè)務(wù)透明的,那么風(fēng)險(xiǎn)就很低值得考慮。
分區(qū)負(fù)載均衡
隨著時(shí)間的推移,數(shù)據(jù)庫(kù)一直在發(fā)生各種變化。如QPS增加,數(shù)據(jù)集更大,或者新增/替換機(jī)器等。無(wú)論哪種都需要將部分?jǐn)?shù)據(jù)分區(qū)和相應(yīng)的請(qǐng)求從一個(gè)節(jié)點(diǎn)移動(dòng)到另外一個(gè)節(jié)點(diǎn),這個(gè)過(guò)程稱(chēng)為分區(qū)的再平衡(rebalance)。業(yè)務(wù)對(duì)再平衡的要求就是平衡過(guò)程中對(duì)業(yè)務(wù)當(dāng)前讀寫(xiě)影響要可控,數(shù)據(jù)讀寫(xiě)服務(wù)不能中斷。還有一點(diǎn)就是為了再平衡應(yīng)盡可能少的遷移數(shù)據(jù)。
前面兩個(gè)要求都不難滿(mǎn)足,最后一個(gè)要求就考驗(yàn)各個(gè)分區(qū)方案的靈活度了。當(dāng)分區(qū)粒度是存儲(chǔ)級(jí)別的Region時(shí),分區(qū)遷移的粒度就是Region,這個(gè)對(duì)業(yè)務(wù)也是透明的;分區(qū)粒度是分區(qū)時(shí),這個(gè)取決于各個(gè)產(chǎn)品對(duì)節(jié)點(diǎn)資源管理的設(shè)計(jì)。比如說(shuō)有的設(shè)計(jì)可以做到只需要遷移分區(qū)就可以調(diào)整各個(gè)節(jié)點(diǎn)的資源利用率和負(fù)載;如果分區(qū)方案是分庫(kù)分表,此時(shí)分區(qū)粒度是分表。但是數(shù)據(jù)遷移的單位通常還是實(shí)例,利用數(shù)據(jù)庫(kù)原生復(fù)制能力搭建新的級(jí)聯(lián)備實(shí)例,然后新老實(shí)例分別刪除一半分庫(kù)數(shù)據(jù)。這里就遷移了不必要的很多數(shù)據(jù)分區(qū)。
分區(qū)訪問(wèn)路由
現(xiàn)在數(shù)據(jù)分區(qū)方案已經(jīng)確定,業(yè)務(wù)數(shù)據(jù)分布在多個(gè)節(jié)點(diǎn)上。業(yè)務(wù)應(yīng)用訪問(wèn)數(shù)據(jù)庫(kù)如何連接呢?再分區(qū)負(fù)載均衡發(fā)生后部分分區(qū)節(jié)點(diǎn)發(fā)生變化,業(yè)務(wù)應(yīng)用是否要修改連接?這個(gè)就是分區(qū)訪問(wèn)路由問(wèn)題,是分布式數(shù)據(jù)庫(kù)的基本能力。理論上分區(qū)訪問(wèn)路由有三種方案。一是每個(gè)節(jié)點(diǎn)都可以進(jìn)行路由轉(zhuǎn)發(fā)(如果請(qǐng)求的數(shù)據(jù)不在該節(jié)點(diǎn)上,該節(jié)點(diǎn)可以轉(zhuǎn)發(fā)應(yīng)用請(qǐng)求到正確的節(jié)點(diǎn)上);二是設(shè)置一個(gè)中心模塊負(fù)責(zé)接受請(qǐng)求并轉(zhuǎn)發(fā)到正確的節(jié)點(diǎn)上;三是應(yīng)用自己獲取分布式數(shù)據(jù)庫(kù)所有分區(qū)的節(jié)點(diǎn)信息,直接連接對(duì)應(yīng)的節(jié)點(diǎn),不需要其他組件提供路由功能。
大部分分布式數(shù)據(jù)庫(kù)架構(gòu),選擇了第二種方案,有一個(gè)負(fù)責(zé)分區(qū)路由訪問(wèn)的模塊。有些產(chǎn)品同時(shí)支持這三種方案。 針對(duì)分區(qū)路由問(wèn)題情況還可能更復(fù)雜。如一個(gè)事務(wù)有多條SQL時(shí)該路由到哪個(gè)節(jié)點(diǎn)。此外就是如果負(fù)責(zé)路由的節(jié)點(diǎn)故障,或者分區(qū)所在節(jié)點(diǎn)故障,這個(gè)路由不可用或者失效時(shí)會(huì)如何恢復(fù)路由服務(wù)。
SQL線性擴(kuò)展能力
當(dāng)數(shù)據(jù)分區(qū)方案確定、分區(qū)路由問(wèn)題也解決了后,運(yùn)維和業(yè)務(wù)架構(gòu)為業(yè)務(wù)的搭建了一個(gè)好的分布式數(shù)據(jù)庫(kù)環(huán)境。很多業(yè)務(wù)誤以為用上分布式數(shù)據(jù)庫(kù)后,就一定會(huì)很好,或者擴(kuò)容后業(yè)務(wù)的性能也能相應(yīng)的提升。實(shí)際使用經(jīng)驗(yàn)并不一定如此。還是前面那句話使用分區(qū)方案主要是獲得擴(kuò)展性,其關(guān)鍵就是分區(qū)分布在更多的節(jié)點(diǎn)上,能利用上更多節(jié)點(diǎn)的能力。
但這個(gè)并不是指讓單個(gè)SQL利用更多節(jié)點(diǎn)的能力。舉個(gè)例子在OLAP業(yè)務(wù)里,一條SQL 如果能讓很多節(jié)點(diǎn)同時(shí)提供服務(wù),其性能當(dāng)然是最好的。不過(guò)這樣的SQL的并發(fā)不能太多,否則很容易讓所有節(jié)點(diǎn)都很忙。即使分布式數(shù)據(jù)庫(kù)擴(kuò)容了節(jié)點(diǎn)將分區(qū)進(jìn)一步打散,由于業(yè)務(wù)的訪問(wèn)壓力和數(shù)據(jù)量也會(huì)增加很多,很可能依然是每個(gè)SQL同時(shí)讓所有節(jié)點(diǎn)為其服務(wù),這個(gè)SQL的吞吐量并不會(huì)隨著這個(gè)節(jié)點(diǎn)數(shù)量的擴(kuò)容而得到相應(yīng)的提升。
分布式數(shù)據(jù)庫(kù)的優(yōu)勢(shì)在于對(duì)于空間問(wèn)題和請(qǐng)求訪問(wèn)問(wèn)題分而治之。針對(duì)每個(gè)分區(qū)的訪問(wèn),由該分區(qū)所在的節(jié)點(diǎn)響應(yīng)即可。即使該SQL 并發(fā)很高,由于訪問(wèn)的是不同的分區(qū),分別由不同的節(jié)點(diǎn)提供服務(wù)。每個(gè)節(jié)點(diǎn)自身也有一定能力滿(mǎn)足一定的QPS,所有節(jié)點(diǎn)集中在一起就能提供更大的QPS。這個(gè)時(shí)候如果擴(kuò)容節(jié)點(diǎn)數(shù)量,該SQL總的QPS也能獲得相應(yīng)的提升。這是分布式數(shù)據(jù)庫(kù)里最好的情形。
第二個(gè)例子根據(jù)PK 訪問(wèn)表,并且PK還是主鍵等。通常我們都建議分庫(kù)分表或者分區(qū)時(shí),業(yè)務(wù)SQL盡量帶上拆分鍵就是這個(gè)道理。但是如果業(yè)務(wù)場(chǎng)景確實(shí)無(wú)法帶上拆分鍵,除了強(qiáng)制掃描所有分區(qū)外,還有個(gè)解決方案就是全局索引表。全局索引是獨(dú)立于數(shù)據(jù)分區(qū)存儲(chǔ)的,全局索引可以避免掃描不必要的分區(qū),負(fù)面作用就是業(yè)務(wù)分區(qū)的寫(xiě)操作很可能帶來(lái)分布式事務(wù)。
以上兩個(gè)例子就是分布式數(shù)據(jù)庫(kù)里SQL的先行擴(kuò)展能力的兩個(gè)極端。前一個(gè)場(chǎng)景SQL沒(méi)有擴(kuò)展能力,后一個(gè)SQL的擴(kuò)展能力幾乎是百分百。大部分SQL的先行擴(kuò)展能力就界于兩者之間。比如說(shuō)SQL里是分區(qū)列的IN條件。這個(gè)SQL的先行擴(kuò)展能力取決于這個(gè)INLIST的數(shù)據(jù)特點(diǎn)。如果恰好每次都是命中同一個(gè)分區(qū),那跟分區(qū)列等值訪問(wèn)效果一樣好;如果INLIST的數(shù)據(jù)命中絕大部分分區(qū),那就接近OLAP 場(chǎng)景的那個(gè)SQL。有些業(yè)務(wù)增長(zhǎng)后,這個(gè)INLIST的長(zhǎng)度基本不變。比如說(shuō)人口業(yè)務(wù),雖然總?cè)丝诘募ぴ?#xff0c;但每個(gè)家庭的子女?dāng)?shù)量大部分在1-2。這是一類(lèi)特點(diǎn),訪問(wèn)這個(gè)子女?dāng)?shù)據(jù)的SQL的先行擴(kuò)展能力會(huì)很好。另外一個(gè)例子就是買(mǎi)家訂單查詢(xún)業(yè)務(wù)。10年前每個(gè)買(mǎi)家一段時(shí)間的訂單數(shù)量可能就幾個(gè),如今每個(gè)買(mǎi)家一段時(shí)間的平均訂單數(shù)量可能在幾十或幾百。
比INLIST 更復(fù)雜的邏輯就是表連接。 表連接時(shí)的條件是否是分區(qū)列,每個(gè)具體的連接值會(huì)相應(yīng)命中多少個(gè)分區(qū),是否有分布式執(zhí)行計(jì)劃等等。都會(huì)影響這個(gè)SQL的線性擴(kuò)展能力。
對(duì)于無(wú)業(yè)務(wù)規(guī)則的分區(qū)方案,雖然分區(qū)對(duì)業(yè)務(wù)是透明的,但不可否認(rèn)的是數(shù)據(jù)分區(qū)是分布在不同的節(jié)點(diǎn)上,只要業(yè)務(wù)讀寫(xiě)這些數(shù)據(jù),數(shù)據(jù)分布特點(diǎn)就會(huì)影響到SQL的性能。對(duì)于業(yè)務(wù)而言,該如何選擇?如果業(yè)務(wù)通過(guò)分區(qū)策略控制數(shù)據(jù)分區(qū)分布特點(diǎn),能夠獲得更高的性能,業(yè)務(wù)是否愿意選擇會(huì)影響分布式數(shù)據(jù)庫(kù)的選型。而不同分區(qū)方案在運(yùn)維方面的特點(diǎn)也不一樣,是影響選型的另外一個(gè)因素,這里就不細(xì)說(shuō)。
螞蟻的分布式數(shù)據(jù)庫(kù)最佳實(shí)踐
螞蟻金服的業(yè)務(wù)規(guī)模非常大,業(yè)務(wù)模塊劃分非常細(xì)。以網(wǎng)商銀行非常核心的交易、賬務(wù)和支付模塊舉例,每個(gè)業(yè)務(wù)模塊的數(shù)據(jù)經(jīng)分布式數(shù)據(jù)庫(kù)中間件(SOFA的DBP)拆分為多個(gè)OceanBase租戶(hù)(實(shí)例)下百庫(kù)百表,每個(gè)表同時(shí)變更為OceanBase自身的分區(qū)表,分為100個(gè)分區(qū)。總共有多個(gè)OceanBase集群,每個(gè)集群橫跨杭州上海和深圳五機(jī)房,并同時(shí)提供服務(wù)。這里的數(shù)據(jù)總共分為10000個(gè)分區(qū),不同分庫(kù)下的數(shù)據(jù)分區(qū)的Leader副本分別位于不同的機(jī)房。不同分表之間可以分別進(jìn)行結(jié)構(gòu)變更(灰度發(fā)布能力),不同OceanBase租戶(hù)甚至集群之間是物理隔離的,這是金融核心業(yè)務(wù)拆分有使用分庫(kù)分表的第一個(gè)原因。
業(yè)務(wù)層面數(shù)據(jù)是按用戶(hù)維度拆分的,不同的用戶(hù)訪問(wèn)不同的機(jī)房的應(yīng)用和數(shù)據(jù)。業(yè)務(wù)層面的流量分配規(guī)則和數(shù)據(jù)分區(qū)Leader副本分配規(guī)則保持一致并聯(lián)動(dòng),實(shí)現(xiàn)了任意時(shí)刻的在線業(yè)務(wù)流量機(jī)房間比例調(diào)整。這是拆分使用分庫(kù)分表的第二個(gè)原因。
OceanBase集群在螞蟻金服業(yè)務(wù)里的核心作用是在數(shù)據(jù)庫(kù)層面解決數(shù)據(jù)副本三地分布的強(qiáng)一致和高可用切換問(wèn)題,并且提供了在線分區(qū)遷移和租戶(hù)彈性伸縮能力。
后記
本文首先針對(duì)分布式數(shù)據(jù)庫(kù)種的數(shù)據(jù)存在的兩種形式副本(復(fù)制產(chǎn)生的)和分區(qū)(分區(qū)產(chǎn)生的)進(jìn)行區(qū)分。然后總結(jié)了分區(qū)方案需要考慮的幾個(gè)點(diǎn):分區(qū)粒度、分區(qū)策略、分區(qū)遷移和負(fù)載均衡、分區(qū)數(shù)量和分區(qū)路由問(wèn)題等。即使這些都考慮好了,也只是分布數(shù)據(jù)庫(kù)這個(gè)初局做好了。后面業(yè)務(wù)能否發(fā)揮分布式數(shù)據(jù)庫(kù)的優(yōu)勢(shì)就取決于業(yè)務(wù)SQL的寫(xiě)法是否有很好的線性擴(kuò)展能力。最后簡(jiǎn)單總結(jié)了螞蟻金服支付寶和網(wǎng)上銀行在分布式數(shù)據(jù)庫(kù)架構(gòu)方面的最佳實(shí)踐。
原文鏈接
本文為云棲社區(qū)原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載。
總結(jié)
以上是生活随笔為你收集整理的分布式数据库选型——数据水平拆分方案的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 源码分析RocketMQ ACL实现机制
- 下一篇: Flink State 有可能代替数据库