分佈式數據庫選型——數據水平拆分方案

概述

水平拆分的概念隨着分佈式數據庫的推廣已爲大部分人熟知。分庫分表、異構索引、小表廣播、這些功能幾乎是產品功能需求標配。然而有些客戶使用分佈式數據庫後的體驗不盡如意。
本文嘗試從數據的角度總結分佈式數據的複製(replication)和分區(partition)技術原理和方案,其中分區也有稱爲分片(sharding),但願能引發讀者一些思考,在分佈式數據庫選型中能注意這些細節的區別,選擇適合業務的數據水平拆分方案。算法

分佈式數據庫架構

分佈式數據庫以集羣形式存在,有多個節點。集羣架構有共享磁盤架構(shared-disk)和無共享架構(shared-nothing)。後者有時也稱爲水平擴展(horizontal scale)或向外擴展(scale out),本文主要總結無共享架構方案。數據庫

無共享架構的各個節點之間的通訊都是軟件層面使用網絡實現,不一樣產品在架構不一樣致使這個細節也不一樣。有些架構是計算與存儲分離。計算節點特色是無狀態(即數據不要求持久化),經過集羣方式管理,能夠水平擴展;存儲節點有數據,使用複製和分區技術,節點間任務集中調度或者獨立交互。瞭解這個架構細節均可用性分析會更加具體。具體分佈式數據庫架構有哪些請參考《一些關係數據庫的架構總結》。網絡

這裏節點的實際體現形式能夠是一個機器,也能夠是機器上的一個實例。好比說有些數據庫支持單機安裝多個實例,如MySQL。每一個節點具有必定的資源和能力。資源指的是CPU、內存和磁盤,能力是提供數據讀寫和存儲能力。分佈式數據庫須要把多個節點的能力彙集到一塊兒集中管理,只是不一樣分佈式數據庫產品對資源的管理能力各有特色。架構

在分佈式數據庫裏,數據隨處可見,這是最容易讓人混淆的地方。由於數據通過複製和分區後會有兩種存在形式:副本(replica)和分區(partition)。併發

數據的複製(replication)

複製(replication)指在幾個不一樣的節點上保存數據的相同副本(replica)。複製提供了冗餘的能力。其做用一是提供高可用能力:若是一個節點不可用,剩餘的節點能夠快速提供數據服務。做用二是提供讀寫分離能力。常見的有兩副本和三副本架構。負載均衡

多個副本內容相同,角色會有區分。常見的是一個副本是Leader角色(有的也稱主副本),默認提供讀寫服務;其餘副本是Follower角色(有的也稱備副本),默認不提供服務。這種架構也稱爲基於單Leader的(Single Leader-based)。還有其餘架構是多Leader的,每一個Leader都有數據要複製到其餘Leader或Follower,這種架構會有個明顯的問題就是數據衝突處理。若是產品層面不處理,用戶直接使用風險會很高。運維

後面討論的是前者:基於單Leader副本架構。異步

多副本之間數據同步不是依賴業務多寫,而是採用副本間複製事務日誌(Redo)技術。複製的方式有同步複製和異步複製。使用同步複製方式,備副本要收到Redo並落盤主副本才能提交,也叫強同步;使用異步複製方式,Follower副本相對Leader副本內容會有延時,具體延時多少取決於Leader副本上事務量、網絡傳輸速度、Follower副本所在節點的負載和能力。強同步的缺點時主副本寫性能會降低,同時若是備副本不可用主副本也不能提供服務(變相的解決方案是複製方式降級爲異步複製)。分佈式

傳統關係型數據庫還有一種用法一主兩備架構,使用同步複製,只要任何一個備副本收到Redo,主副本的事務就能夠提交。這個方案優勢是保障了數據在多個副本中存在,高可用時有候選副本,也不用擔憂掛掉一個備副本會影響主副本。它的缺點是不能自動知道哪一個候選副本擁有主副本最新最全的數據,也不強制要求兩個備副本都要擁有所有數據。函數

還有一類三副本架構在複製時使用的是Paxos協議,三副本會就Redo落盤事件進行投票,有兩個副本成功了Leader副本的事務便可提交。這個表面上跟上面傳統一主兩備的三副本效果同樣,實際上仍是有區別的。區別一是使用Paxos協議時,若是Leader副本自身投票慢了,兩個Follower副本投票成功,Leader副本的事務也是能提交的;區別二是第三個副本最終也必須寫Redo成功,不然其狀態就是異常,產品自身能夠發現並自動修復(如從新建立一個副本);區別三是使用Paxos協議時,在Leader副本不可用時還能夠自動選出新的Leader副本而且擁有老Leader副本的最新數據。這裏其實說的是高可用機制。一樣,這裏對用戶而言也不知道哪一個Follower副本擁有最新最全的數據,若是訪問Follower副本(讀寫分離),也可能發現數據有延時。

大部分數據庫作副本複製使用的是Redo,也稱爲物理同步。在應用Redo的時候直接是數據塊變動。使用物理同步機制的備副本是不提供寫服務,不能修改。還有一類複製使用的是Binlog,也稱爲邏輯同步。Binlog裏只包含已提交的事務,而且在應用的時候是經過執行SQL。使用邏輯同步的備副本一般也多是主副本,能夠修改(如MySQL的雙向複製架構Master-Master)。若是目標端數據不對,應用SQL會失敗,這個複製就會中斷須要人介入處理。這也進一步加深了主備副本不一致的機率。

關於副本角色的粒度,有多種實現方案。

傳統關係數據庫主備架構,主副本或備副本的粒度就是實例。對於主實例(Primary)而言,裏面全部數據庫(或SCHEMA)的全部表的角色都是主;備實例(Standby)裏數據則都是備副本。若是發生高可用切換,業務會中斷幾十秒或幾分鐘而後恢復(須要人工處理或自動化腳本處理)。

還有一種粒度是到表。即一個節點內有些表是Leader副本,有些表是Follower副本,這樣這個節點就不能簡單的說是主節點(實例)或備節點(實例)。這個副本角色細節業務也是能夠獲取的,若是發生高可用切換,業務會中斷十幾秒而後恢復。

還有一種粒度是存儲級別的定長塊。即一個節點的存儲裏,部分數據塊是Leader副本,部分數據塊是Follower副本。這種對業務就徹底透明,業務基本不感知高可用切換。

數據的分區(partition)

上面總結的是數據的複製(冗餘,多副本),對於很是大的數據集(表)或者很是高的訪問量(QPS),僅僅靠複製是不夠的,還須要對數據進行分區(partition),也稱爲分片(sharding)。

分區粒度

首先這裏的分區(partition)是一種抽象概念,在不一樣數據庫產品裏這個體現是不同的。如在MongoDB, Elasticsearch中體現爲分片(shard),在HBase中體現爲區域塊(Region),Bigtable中體現爲表塊(tablet),ORACLE中體現爲分區(partition),Couchbase中體現爲虛擬桶(vBucket)。可見不一樣的數據庫產品數據分區的粒度不一樣。在分佈式關係數據庫中間件中,分片的粒度是分表(物理表);在真正的分佈式關係數據庫裏,分片的粒度有分區(partition,同ORACLE)或者區域塊(Region)。

分區粒度對業務研發的使用體驗影響很大。

好比說中間件經常使用分庫分表方案,使用時對開發和運維會有一些要求。如建不少同構的表並後期維護、要求SQL帶上拆分鍵,還有一些功能限制(如跨庫JOIN問題)、底層存儲節點用的數據庫自身高可用和多副本的數據一致問題等等。不一樣的中間件產品能力上也有區別,互聯網大廠的產品因爲內部場景培育好久,作的相對成熟一些。

體驗最好的分區粒度就是存儲級別的Region,業務研發徹底不用關心分片細節,也沒法干預分片細節。當有些場景追求性能須要干預數據分佈特色時就很差處理。

介入這兩種策略之間的就是分區。物理上業務只要建立一個分區表,根據業務特色指定分區策略(包含分區列、拆分算法、分區數目等)。

數據複製是爲了冗餘和高可用,數據分區主要是爲了可擴展性。無論使用哪一種分區方案,業務的每條數據(記錄)屬於且僅屬於一個分區(或分片sharding),同一個分區(分片)只會存在於一個節點。前面說了每一個節點表明了必定的資源和能力。當複製和分區(分片)一塊兒使用的時候,注意區分你看到的數據。

分區策略

分區的目標是將大量數據和訪問請求均勻分佈在多個節點上。若是每一個節點均勻承擔數據和請求,那麼理論上10個節點就應該能承擔10倍於單節點的數據量和訪問量。這個理論是忽略了複製產生的Follower副本的存在。Follower副本的空間和內存是不可能跟其餘Leader副本共享的,可是計算能力(CPU)是能夠的。當全部節點都提供服務的時候(多活),是計算資源最大利用。

然而若是分區是不均勻的,一些分區的數據量或者請求量會相對比較高,出現數據偏斜(skew),這個可能致使節點資源利用率和負載也不均衡。偏斜集中的數據咱們又稱爲熱點數據。避免熱點數據的直接方法就是數據存儲時隨機分配(沒有規則)給節點,缺點是讀取的時候不知道去哪一個分區找該記錄,只有掃描全部分區了,因此這個方法意義不大。實際經常使用的分區策略都是有必定的規則。

這個規則能夠是業務規則,也能夠不是。

業務規則的分區首先是選取一個或一組列做爲分區鍵,而後選取拆分方法。好比說根據鍵的範圍(Range)分區,分區數量和邊界時肯定的(後期還能夠新增分區)。好處時針對分區鍵的範圍掃描性能會比較好。分佈式數據庫中間件的分庫分表、分區表的分區都支持RANGE 拆分函數。各個產品拆分細節上面會有一些創新。Range分區的缺點是某些特定的訪問模式會致使熱點。好比說根據時間列作RANGE分區,業務寫入和讀寫數據集中在最近的時間,就可能致使各個分區負載不均衡。這只是一個缺點,業務層面還要考慮這樣作的好處。好比說刪除歷史分區比較快。

還有種拆分方法是散列(HASH)分區,分區數量和邊界是肯定的(後期能夠作分區分裂)。這時各個數據的分佈是否均衡就取決於各個產品實現機制。大部分作法是使用一個散列(HASH)函數對Key計算一個值,而後針分段存儲。

有的產品會使用這個HASH值對分區數取模,這個方法可能引發分區數據分佈不均勻(若MySQL的Key分區)。此外若是要調整分區數,則須要移動全部數據。ORACLE的HASH分區時會先選取最接近分區數的一個2的冪值,對於分區數大於這個值的分區,會從前面分區裏調過來。因此ORACLE 建議HASH分區數爲2的冪。M有SQL建議Key分區數爲奇數時數據分佈最均勻。

此外在現有分區下還能夠再作一次分區,分區鍵和分區方法均可以不同。一般稱爲兩級分區。好比說分庫分表時,分庫和分表策略不同就是兩級分區;分區表也支持兩級分區。

有業務規則的分區方案的特色就是使用上。SQL若是要性能好建議帶上分區鍵,這樣分佈式數據庫才能夠直接定位到所訪問數據所在的分片;不然,數據庫就要掃描全部分區去查詢數據。一般分區鍵只能選取一個或一組業務字段,表明的是一個業務維度,那麼另一種業務維度的SQL請求性能就會很差。個別分佈式數據庫產品在HASH 方法上支持兩種維度的分區列,其前提是在業務構造數據時讓這兩個列有着內部一致的分區邏輯。

詳情能夠參考《說說分庫分表的一個最佳實踐》。

另一種分區策略就是無業務規則的,在存儲級別按塊的大小切分爲多個定長塊(Region)。這個分區對業務而言就是透明的,因此使用體驗上會相對好一些。

不過,分佈式數據庫裏的數據分區除了存儲數據還要提供讀寫服務。業務讀寫數據的SQL自己是帶業務邏輯的,若是一次SQL請求訪問的數據分散到多個分區,而這些分區又散落在不一樣的節點上,不可避免的會發生跨節點的請求。若是是多表鏈接,這種情形更容易出現。若是這個業務請求有事務,那這就產生了分佈式事務。分佈式事務解決方案有兩種,強一致的兩階段提交(XA)方案和最終一致的TCC方案。詳情請參考《說說數據庫事務和開發(下)—— 分佈式事務》。

這裏主要提示跨節點的請求帶來的性能衰減。固然,硬件方面萬兆網卡加RDMA技術下網絡延時已經縮小不少,可是當分佈式數據庫的請求量(QPS)很是高時,或者分佈式數據庫是多機房部署(好比說兩地三中心)時,跨機房的網絡延時仍是不可忽視,跨節點的請求帶來的性能衰減也會很明顯。因此有業務規則的分區策略能夠提供策略給業務控制本身的數據分區分佈特色,很是適合作異地多活和單元化類業務。此外還有個經常使用的規避跨節點請求讀的方法就是小表廣播,即將個別沒有分區的表的數據複製到其餘分區所在的節點,這樣相關業務數據分區的JOIN就是在本地節點內部完成。這裏就看複製使用的是物理同步仍是邏輯同步,以及同步的延時是否知足業務需求。

分區數量

關於分區數量也須要評估。若是是無規則的分區策略,因爲每一個分區(分片)是定長塊,那麼分區數量就由總數據大小除以定長塊大小,對業務也是透明的。這裏總結的是有業務規則的分區的數量。

使用分區的目的是爲了擴展性,具體就是能將不一樣分區分散多多個節點上,發揮多個節點的資源和能力。因此分區數必定要大於可用的資源節點數,爲了考慮到未來分佈式數據庫可能會擴容,分區數應該是數倍於當前規劃的節點數。這是一個總的指導思想。因爲不一樣的分佈式數據庫其節點的表示方法不同,實施的時候會略有不一樣。

好比說在分佈式數據庫中間件架構裏,數據存儲的節點是實例,數據分區的粒度是分表(物理表),中間還有一層分庫的維度。分佈式數據庫實例:總物理實例數:總物理分庫數:總物理分表數=1:M:N:X 。X是分區的數量,N 是總分庫數。X 是固定的,若是要調整分區數,成本很是高,因此通常都是提早規劃好。N 是總分庫數,是2的冪。 M 是實例的數量,也建議是2的冪,決定了最大能用多少節點的資源。 N/M 的結果決定了將來能擴容的倍數。分佈式數據庫中間件因爲數據分區落在具體的節點後就不能自由移動,其擴容方式可能是對每一個實例一分爲二,最好的途徑就是利用數據庫(MySQL)自身的主從複製搭建新的備實例擴容節點數。

此外分區數還要考慮到單個分區的容量和請求量是否知足需求。即分區是否到位。這個也是須要業務評估的。在使用分區表的分區方案的分佈式數據庫裏,分區數也是結合上面兩點考慮的。

固然分區數太大了,可能會增長分佈數據庫內部管理成本。分區數量跟分區粒度剛好是相反關係,兩者都須要取一個合適的值。

分區數量一旦肯定後,調整的成本很是高,一般會引發數據重分佈。有些產品能夠針對特定類型的分區作分區分裂。如RANGE分區能夠分裂爲兩個RANGE, HASH分區也能夠一分爲二。只要這個分區分裂的邏輯是數據庫內部邏輯實現,保證數據不丟,且對業務透明的,那麼風險就很低值得考慮。

分區負載均衡

隨着時間的推移,數據庫一直在發生各類變化。如QPS增長,數據集更大,或者新增/替換機器等。不管哪一種都須要將部分數據分區和相應的請求從一個節點移動到另一個節點,這個過程稱爲分區的再平衡(rebalance)。業務對再平衡的要求就是平衡過程當中對業務當前讀寫影響要可控,數據讀寫服務不能中斷。還有一點就是爲了再平衡應儘量少的遷移數據。

前面兩個要求都不難知足,最後一個要求就考驗各個分區方案的靈活度了。當分區粒度是存儲級別的Region時,分區遷移的粒度就是Region,這個對業務也是透明的;分區粒度是分區時,這個取決於各個產品對節點資源管理的設計。好比說有的設計能夠作到只須要遷移分區就能夠調整各個節點的資源利用率和負載;若是分區方案是分庫分表,此時分區粒度是分表。可是數據遷移的單位一般仍是實例,利用數據庫原生複製能力搭建新的級聯備實例,而後新老實例分別刪除一半分庫數據。這裏就遷移了沒必要要的不少數據分區。

分區訪問路由

如今數據分區方案已經肯定,業務數據分佈在多個節點上。業務應用訪問數據庫如何鏈接呢?再分區負載均衡發生後部分分區節點發生變化,業務應用是否要修改鏈接?這個就是分區訪問路由問題,是分佈式數據庫的基本能力。理論上分區訪問路由有三種方案。一是每一個節點均可以進行路由轉發(若是請求的數據不在該節點上,該節點能夠轉發應用請求到正確的節點上);二是設置一箇中心模塊負責接受請求並轉發到正確的節點上;三是應用本身獲取分佈式數據庫全部分區的節點信息,直接鏈接對應的節點,不須要其餘組件提供路由功能。

大部分分佈式數據庫架構,選擇了第二種方案,有一個負責分區路由訪問的模塊。有些產品同時支持這三種方案。 針對分區路由問題狀況還可能更復雜。如一個事務有多條SQL時該路由到哪一個節點。此外就是若是負責路由的節點故障,或者分區所在節點故障,這個路由不可用或者失效時會如何恢復路由服務。

SQL線性擴展能力

當數據分區方案肯定、分區路由問題也解決了後,運維和業務架構爲業務的搭建了一個好的分佈式數據庫環境。不少業務誤覺得用上分佈式數據庫後,就必定會很好,或者擴容後業務的性能也能相應的提高。實際使用經驗並不必定如此。仍是前面那句話使用分區方案主要是得到擴展性,其關鍵就是分區分佈在更多的節點上,能利用上更多節點的能力。

但這個並非指讓單個SQL利用更多節點的能力。舉個例子在OLAP業務裏,一條SQL 若是能讓不少節點同時提供服務,其性能固然是最好的。不過這樣的SQL的併發不能太多,不然很容易讓全部節點都很忙。即便分佈式數據庫擴容了節點將分區進一步打散,因爲業務的訪問壓力和數據量也會增長不少,極可能依然是每一個SQL同時讓全部節點爲其服務,這個SQL的吞吐量並不會隨着這個節點數量的擴容而獲得相應的提高。

分佈式數據庫的優點在於對於空間問題和請求訪問問題分而治之。針對每一個分區的訪問,由該分區所在的節點響應便可。即便該SQL 併發很高,因爲訪問的是不一樣的分區,分別由不一樣的節點提供服務。每一個節點自身也有必定能力知足必定的QPS,全部節點集中在一塊兒就能提供更大的QPS。這個時候若是擴容節點數量,該SQL總的QPS也能得到相應的提高。這是分佈式數據庫裏最好的情形。

第二個例子根據PK 訪問表,而且PK仍是主鍵等。一般咱們都建議分庫分表或者分區時,業務SQL儘可能帶上拆分鍵就是這個道理。可是若是業務場景確實沒法帶上拆分鍵,除了強制掃描全部分區外,還有個解決方案就是全局索引表。全局索引是獨立於數據分區存儲的,全局索引能夠避免掃描沒必要要的分區,負面做用就是業務分區的寫操做極可能帶來分佈式事務。

以上兩個例子就是分佈式數據庫裏SQL的先行擴展能力的兩個極端。前一個場景SQL沒有擴展能力,後一個SQL的擴展能力幾乎是百分百。大部分SQL的先行擴展能力就界於二者之間。好比說SQL裏是分區列的IN條件。這個SQL的先行擴展能力取決於這個INLIST的數據特色。若是剛好每次都是命中同一個分區,那跟分區列等值訪問效果同樣好;若是INLIST的數據命中絕大部分分區,那就接近OLAP 場景的那個SQL。有些業務增加後,這個INLIST的長度基本不變。好比說人口業務,雖然總人口的激增,但每一個家庭的子女數量大部分在1-2。這是一類特色,訪問這個子女數據的SQL的先行擴展能力會很好。另一個例子就是買家訂單查詢業務。10年前每一個買家一段時間的訂單數量可能就幾個,現在每一個買家一段時間的平均訂單數量可能在幾十或幾百。

比INLIST 更復雜的邏輯就是錶鏈接。 錶鏈接時的條件是不是分區列,每一個具體的鏈接值會相應命中多少個分區,是否有分佈式執行計劃等等。都會影響這個SQL的線性擴展能力。

對於無業務規則的分區方案,雖然分區對業務是透明的,但不能否認的是數據分區是分佈在不一樣的節點上,只要業務讀寫這些數據,數據分佈特色就會影響到SQL的性能。對於業務而言,該如何選擇?若是業務經過分區策略控制數據分區分佈特色,可以得到更高的性能,業務是否願意選擇會影響分佈式數據庫的選型。而不一樣分區方案在運維方面的特色也不同,是影響選型的另一個因素,這裏就不細說。

螞蟻的分佈式數據庫最佳實踐

螞蟻金服的業務規模很是大,業務模塊劃分很是細。以網商銀行很是核心的交易、帳務和支付模塊舉例,每一個業務模塊的數據經分佈式數據庫中間件(SOFA的DBP)拆分爲多個OceanBase租戶(實例)下百庫百表,每一個表同時變動爲OceanBase自身的分區表,分爲100個分區。總共有多個OceanBase集羣,每一個集羣橫跨杭州上海和深圳五機房,並同時提供服務。這裏的數據總共分爲10000個分區,不一樣分庫下的數據分區的Leader副本分別位於不一樣的機房。不一樣分表之間能夠分別進行結構變動(灰度發佈能力),不一樣OceanBase租戶甚至集羣之間是物理隔離的,這是金融核心業務拆分有使用分庫分表的第一個緣由。

業務層面數據是按用戶維度拆分的,不一樣的用戶訪問不一樣的機房的應用和數據。業務層面的流量分配規則和數據分區Leader副本分配規則保持一致並聯動,實現了任意時刻的在線業務流量機房間比例調整。這是拆分使用分庫分表的第二個緣由。

OceanBase集羣在螞蟻金服業務裏的核心做用是在數據庫層面解決數據副本三地分佈的強一致和高可用切換問題,而且提供了在線分區遷移和租戶彈性伸縮能力。

後記

本文首先針對分佈式數據庫種的數據存在的兩種形式副本(複製產生的)和分區(分區產生的)進行區分。而後總結了分區方案須要考慮的幾個點:分區粒度、分區策略、分區遷移和負載均衡、分區數量和分區路由問題等。即便這些都考慮好了,也只是分佈數據庫這個初局作好了。後面業務可否發揮分佈式數據庫的優點就取決於業務SQL的寫法是否有很好的線性擴展能力。最後簡單總結了螞蟻金服支付寶和網上銀行在分佈式數據庫架構方面的最佳實踐。


原文連接 本文爲雲棲社區原創內容,未經容許不得轉載。

相關文章
相關標籤/搜索