淺析分散式資料庫

前言

隨著資訊科技的迅猛發展,各行各業產生的資料量呈爆炸式增長,傳統集中式資料庫的侷限性在面對大規模資料處理中逐漸顯露,從而分散式資料庫應運而生。分散式資料庫是在集中式資料庫的基礎上發展起來的,是分散式系統與傳統資料庫技術結合的產物,具有透明性、資料冗餘性、易於擴充套件性等特點,還具 備高可靠、高可用、低成本等方面的優勢,能夠突破傳統資料庫的瓶頸。應用對資料庫的要求越來越高,新的應用要求資料庫不僅具有良好的ACID屬性,還要具有良好的擴充套件性。

資料庫發展歷程

淺析分散式資料庫

簡單一句話總結,整體資料庫的發展經歷了SQL、NoSQL、NewSQL、Distributed SQL。

具體SQL、NoSQL、NewSQL型別有如下分類:

SQL

SQL是關係型資料庫管理系統(RDBMS),顧名思義,它是圍繞關係代數和元組關係演算構建的。70年代以來,它一直是主要的資料庫解決方案,只是最近才有了其他產品的空間。不管有些人說什麼,這意味著它一直能出色地執行廣泛的任務。其主要優點如下:

不同的角色(開發者,使用者,資料庫管理員)使用相同的語言。

不同的RDBMS使用統一標準的語言。

SQL使用一種高階的非結構化查詢語言。。

它堅持 ACID 準則 (原子性,一致性,隔離性,永續性),,這些準則保證了資料庫尤其是每個事務的穩定性,安全性和可預測性。

如你所見,許多SQL的好處來源於它的統一性,舒適性和易用性。即使你只有非常有限的SQL知識(或完全沒有,如果需要),你可以在像 online SQL Query Builder 這樣的特殊工具幫助下使用它。

然而,它的缺點使得它非常不適合某些型別的專案。SQL的主要問題是它難以擴充套件,因為它的效能隨著資料庫的變大而快速下降。分散式也是有問題的。

NoSQL和NewSQL出現的原因之一是,以前的RDBMS的設計不能滿足現代資料庫每秒處理的事務數量。像亞馬遜或阿里巴巴等需要處理驚人資料量的巨頭,以前的RDBMS會在幾分鐘內出現問題。

NoSQL (Not Only SQL)

NoSQL越來越受歡迎,其中最重要的實現是Apache Cassandra,MongoDB等產品。它主要用於解決SQL的可擴充套件性問題。因此,它是沒有架構的並且建立在分散式系統上,這使得它易於擴充套件和分片。

然而,這些好處是以放寬ACID原則為代價的:NoSQL採取最終一致性原則,而不是所有四個引數在每個事務中保持一致。這意味著如果在特定時間段內沒有特定資料項的更新,則最終對其所有的訪問都將返回最後更新的值。這就是這樣的系統通常被描述為提供基本保證的原因(基本可用,軟狀態,最終一致性) — 而不是ACID。

雖然這個方案極大地增加了可用時間和伸縮性,它也會導致資料丟失——這個問題的嚴重程度取決於資料庫伺服器的支援情況和應用程式碼質量。在某些情況下,這個問題十分嚴重。

另一個NoSQL出現的問題是現在有很多型別的NoSQL系統,但它們之間卻幾乎沒有一致性。諸如靈活性,效能,複雜性,伸縮性等等特性在不同系統間差別巨大,這使得甚至是專家在他們之間都很難選擇。不過,當你根據專案特點作出了合適的選擇,NoSQL可以在不顯著丟失穩定性的情況下提供一個遠比SQL系統更高效的解決方案。

NewSQL

人們常常批評 NoSQL“為了倒掉洗澡水,卻把嬰兒一起衝進了下水道”(Throwing the baby out with the bathwater)。SQL 類資料庫應用如此廣泛,為了分散式特性就需要拋棄 SQL 顯得非常得不償失。

因此一些組織開始構建基於 SQL 的分散式資料庫,從表面看它們都支援 SQL,但是根據實現方式,其發展出了兩種路線:NewSQL 和 Distributed SQL。這一講我先介紹前者。

NewSQL 是基於 NoSQL 模式構建的分散式資料庫,它通常採用現有的 SQL 類關係型資料庫為底層儲存或自研引擎,並在此之上加入分散式系統,從而對終端使用者遮蔽了分散式管理的細節。Citus 和 Vitess 就是此種類型的兩個著名案例,在後面的第四個模組中,我會具體介紹。

此外,一些資料庫中介軟體,如 MyCAT、Apache ShardingShpere,由於其完全暴露了底層的關係型資料庫,因此不能將它們稱為 NewSQL 資料庫,不過可以作為此種模式的另類代表。

大概在 2010 年年初的時候,人們嘗試構建此類資料庫。而後,451 ResEArch 的 Matthew Aslett 於 2011 年創造了“NewSQL”這個術語,用於對這些新的“可擴充套件” SQL 資料庫進行定義。

NewSQL 資料庫一般有兩種。

第一種是在一個個獨立執行的 SQL 資料庫例項之上提供了一個自動資料分片管理層。例如,Vitess 使用了 MySQL,而 Citus 使用 PostgreSQL。由於每個獨立例項仍然是單機關係型資料庫,因此一些關鍵特性無法得到完美支援,如本地故障轉移 / 修復,以及跨越分片的分散式事務等。更糟糕的是,甚至一些單機資料庫的功能也無法進行使用,如 Vitess 只能支援子查詢的一個“子集”。

第二種包括 NuoDB、VoltDB 和 Clustrix 等,它們構建了新的分散式儲存引擎,雖然仍有或多或少的功能閹割,但可以給使用者一個完整的 SQL 資料庫體驗。

NewSQL 資料庫最初構建的目的是解決分散式場景下,寫入 SQL 資料庫所面臨的挑戰。它可以使用多個傳統單機 SQL 資料庫作為其儲存節點,在此基礎上構建起可擴充套件的分散式資料庫。在它產生的年代,雲技術還處於起步階段,因此這類 NewSQL 得到了一定程度的發展。但是,隨著多可用區、多區域和多雲的雲部署成為現代應用程式的標準,這些資料庫也開始力不從心起來。

與此同時,像 Google Spanner 和 TiDB 這樣的 Distributed SQL 資料庫的崛起,NewSQL 資料庫的地位就受到了進一步挑戰。因為後者是被設計利用雲組價的特性,並適應在不可靠基礎設施中穩定執行的“雲原生”資料庫。

可以看到 NewSQL 迴歸了以 SQL 為核心的狀態,這次迴歸展示了 SQL 的魅力,即可以穿越數十年時光。但這次革命是不徹底的,我們可以看到傳統單機資料庫的身影,還有對 SQL 功能的閹割。而革命者本身也往往來自應用領域,而不是專業資料庫機構。所以NewSQL 更像是使用者側的狂歡,它可以解決一類問題,但並不完備,需要小心地評估和使用。

Distributed SQL

上面我也提到過 Distributed SQL 資料庫,此種使用的是特殊的底層儲存引擎,來構建水平可伸縮的資料庫。它在 NewSQL 的功能基礎上,往往提供的是“地理分佈”功能,使用者可以跨可用區、區域甚至在全球範圍內分佈資料。CockroachDB、Google的Spanner、OceanBase 和 PingCAP 的 TiDB 就是很好的例子,這些引擎通常比 NewSQL 的目標更高。

但需要強調的是,NoSQL 和 NewSQL 是建立在一個假設上,即構建一個完備功能的分散式資料庫代價是高昂的,需要進行某種妥協。而商用 Distributed SQL 資料庫的目標恰恰是要以合理的成本構建這樣一種資料庫,可以看到它們的理念是針鋒相對的。

相比於典型的 NewSQL,一個 Distributed SQL 資料庫看起來更像一個完整的解決方案。它的功能一般包括可擴充套件性、資料一致性、高可用性、地理級分佈和 SQL 支援,它們並非一些工具的組合。一個合格的 Distributed SQL 資料庫應該不需要額外工具的支援,就可以實現上述功能。

此外,由於 Distributed SQL 天然適合與雲計算相結合,因此一些雲原生資料庫也可以歸為此門類,如 AWS 的 Aurora 等。不論是雲還是非雲資料庫,Distributed SQL 幾乎都是商業資料庫,而 NewSQL 由於其工具的本質,其中開源型別的資料庫佔有不小的比重。

這一方面反映了 Distributed SQL 非常有潛力且具有商業價值,同時也從一個側面說明了它才是黃金年代 SQL 關係型資料庫最為正統的傳承者。

新一代的 SQL 已經冉冉升起,它來自舊時代。但除了 SQL 這一個面孔外,其內部依然發生了翻天覆地的改變。不過這正是 SQL 的魅力:穿越時光,依然為資料庫的核心,也是資料庫經典理論為數不多的遺產。

分散式資料庫發展歷程

2006年,Google釋出了三篇論文,也是公認的大資料的三駕馬車:分散式檔案系統GFS、分散式KV儲存資料庫Big Table以及處理和生成超大資料集的演算法模型MapReduce。

此後,雖然分散式開始成為大家討論的物件,但由於分散式事務的效能以及分散式系統的複雜性,使得分散式資料庫僅在資料量非常大的聯機分析處理(OLAP)場景得到了一些應用。在傳統資料庫領域,仍以Oracle為代表的的集中式資料庫獨霸天下,更是獨領國際市場,阿里便是其在中國最大的客戶。

此後的十年,是分散式資料庫被“冷落”的十年。與其說是被“冷落”,不如說是技術上難以突破,以及培育成本高,當時集中式資料庫的商業化之路越走越順,能夠停下來從頭開始的廠商少之又少。

不過,隨著網際網路時代的加速發展和科技的進步,集中式資料庫的功能開始捉襟見肘。越來越多的企業進行數字化轉型,對業務系統也更加高頻的併發訪問,當產生龐大的資料處理量,集中式資料庫昂貴的成本和儲存、計算極為有限的擴充套件能力開始暴露,企業不得不尋求價效比更高、儲存和計算擴充套件能力更強的資料庫。

2010年,陽振坤在阿里的招募下,開始研發國內第一款全自研式分散式資料庫OceanBase。彼時的分散式技術是真正的無人區,直至2015年,騰訊雲、阿里雲、PingCap等公司才開始在初步探索。

分散式資料庫被“冷落”的10年,正是OceanBase在螞蟻內部打磨的十年。從僅用在淘寶收藏夾一個細小的場景,到支撐了9年的淘寶雙十一,並打破了TPC-C測試的世界紀錄,直至2020年獨立,到目前已經累計了400+客戶。

簡單總結一下,分散式資料庫發展大概經歷了三個階段:

第一代是分散式儲存系統

,也稱為 NoSQL,2013 年之前比較流行,基本思路是犧牲 SQL,犧牲事務、一致性和企業級功能,只支援簡單的 KV 操作從而做到可擴充套件;

第二代是分散式資料庫

,以 Google Spanner 系統為代表,支援可擴充套件的 SQL,在第一代 NoSQL 系統的基礎之上引入了 SQL 和分散式事務,保證強一致性,但是不太注重 SQL 相容性和價效比,單機效能往往比較差;

第三代是透明擴充套件的企業級資料庫

,也就是我說的“下一代企業級分散式資料庫”。以 OceanBase、TiDB 為代表。分散式架構對業務透明,支援完備的相容 MySQL 和 Oracle 的企業級功能,支援 HTAP 混合負載,單機效能很高,且系統架構的效能天花板很高,可以基於該架構追求極致效能。

什麼是分散式資料庫

首先看下百度百科對於分散式資料庫系統的定義:

分散式資料庫系統 (DDBS)包含分散式資料庫管理系統(DDBMS)和分散式資料庫(DDB)。在分散式資料庫系統中,一個應用程式可以對資料庫進行透明操作,資料庫中的資料分別在不同的區域性資料庫中儲存、由不同的 DBMS進行管理、在不同的機器上執行、由不同的作業系統支援、被不同的通訊網路連線在一起。

百度百科

分散式資料庫最早於 20 世紀 80 年代提出,受限於當時 的計算機軟硬體及網路發展水平,資料庫專家 M。Tamer Özsu 和 Patrick Valduriez 在經典著作《分散式資料庫系統原理(第 3 版)》中,把分散式資料庫定義為一群分佈在計算機網路上、 邏輯上相互關聯的資料庫。

隨著資訊科技的發展,集中式數 據庫也正向基於網路的共享叢集路線發展,而市場上的分佈 式資料庫也不僅限於網路分佈、邏輯關聯等特性,經典的分 布式資料庫定義顯然已不能體現分散式資料庫當前技術特 點,難以滿足資料庫種類區分要求。

根據目前我國分散式資料庫技術現狀,我們認為分散式 資料庫是具備分散式事務處理能力、可平滑擴充套件、分佈於計 算機網路且邏輯上統一的資料庫。主要特徵如下:

分散式事務處理

分散式資料庫與集中式資料庫的 主要區別就是是否具備分散式事務的處理能力。透過對資料 庫各種操作的平行計算、全域性事務管理等機制,實現真正的 分散式事務處理,並實現與集中式資料庫一致的 ACID 特性。

平滑擴充套件

分散式資料庫可根據業務的增長需要, 動態擴充套件物理節點,以提升整體處理能力,且擴充套件過程不需 停機,不影響線上業務。理論上可以進行無限擴充套件,擴充套件之 後在邏輯上仍然是一個統一的資料庫。

物理分佈、邏輯統一

分散式資料庫的資料不是存 儲在一個物理節點中,而是儲存在計算機網路上的多個節點 上,且透過網路實現了真正的物理分佈。而邏輯上仍是一個 資料庫,為使用者提供統一的訪問入口,實現對分佈在網路節 點上的資料的統一操作,即使用者可以像使用傳統集中式資料 庫一樣使用分散式資料庫,而不是分別操作多個數據庫。

分散式資料庫架構設計

經典資料庫包含儲存、事務、SQL 這幾個核心引擎,以及基於這些核心引擎之上的資料庫功能和效能、成本、安全、可靠等企業級特性。企業級分散式資料庫在經典資料庫的基礎之上引入了分散式,實現了高可用和可擴充套件。雖然分散式資料庫在原生分散式架構上產生了很多創新技術,但在資料庫的功能、效能、精細化程度上還有一段很長的路要走。

企業級分散式資料庫除了做好分散式,更要堅守資料庫的初心:提升功能相容性和單機效能。經典資料庫的關鍵技術經受住了時間的考驗,例如 SQL 標準、事務模型,企業級分散式資料庫需要向經典資料庫學習 SQL 相容性。今天,分散式領域很熱門的一些技術,例如儲存計算分離、HTAP,最早也出自經典資料庫,且經典資料庫在精細化程度上往往做得更好。

儲存設計

資料庫常用的儲存架構設計模型有三種:

淺析分散式資料庫

shared-Everything

一般針對於單機而言,完全透明的共享 CPU、記憶體和IO等資源,並行能力是三種結構中最差的。

shared-Disk

shared-disk也可以成為shared-storage,每個單元的CPU和記憶體是獨立的,共享磁碟系統,典型產品有Oracle RAC,它是資料共享,可以透過增加節點來提高並行處理能力,擴充套件能力較好。當儲存器介面達到飽和時,增加節點並不能獲得更高的效能。

aurora採用了share storage, shared-storage同樣可以解決 ha 和快速恢復(甚至比選舉還快),特別是對於雲廠商可以一個大 storage 給多個租戶,對使用者便宜對自己省錢。

shared-Nothing

每個處理單元所擁有的資源都是獨立的,不存在共享資源。單元之間透過協議通訊,並行處理和擴充套件能力更好。各個節點相互獨立,各自處理自己的資料,處理後的結果可能向上層彙總或者節點間流轉。

目前包括國內OceanBase、TiDB都採用了Shared-Nothing架構。

Shared-Nothing架構的優勢:

易於擴充套件

內部自動並行處理,無需人工分割槽或最佳化

最最佳化的IO處理

增加節點實現儲存、查詢及載入效能的線性擴充套件

儲存計算分離

相信今天很多人都聽過儲存計算分離,但不同系統的做法差別很大。業界有三種儲存計算分離方案:

中介軟體分庫分表

基於中介軟體分庫分表,後端的資料庫表示儲存,中介軟體表示計算,這種方案是真正的儲存計算分離嗎?顯然不是,我也把這種方案叫做“偽儲存計算分離”,主要有以下兩種模式:

客戶端程式庫方式

該方式在客戶端安裝程式庫,透過客戶端程式庫直接訪問資料,該方式的優點是效能高,缺點是對應用有侵入。訪問示意圖如下:

典型客戶端程式庫中介軟體如阿里的TDDL,本文以TDDL為例,介紹客戶端程式庫方式的資料庫訪問中介軟體的工作原理。

TDDL採用了客戶端庫(即Java。jar包)形式,在Jar包中封裝了分庫分表的邏輯,部署在ibatis、mybatis或者其他ORM框架之下、JDBC Driver之上,是JDBC或持久框架層與底層JDBC Driver之間的互動橋樑。

TDDL的邏輯架構分為三層:Matrix 、Group、Atom。Matrix層負責分庫分表路由,SQL語句的解釋、最佳化和執行,事務的管理規則的管理,各個子表查詢出來結果集的Merge等;Group層負責資料庫讀寫分離、主備切換、權重的選擇、資料保護等功能;Atom層是真正和物理資料庫互動,提供資料庫配置動態修改能力,包括動態建立,新增,減少資料來源等。

當client向資料庫傳送一條SQL的執行語句時,會優先傳遞給Matrix層。由Martix 解釋 SQL語句、最佳化,並根據查詢條件將SQL路由到各個group;各個group根據權重選擇其中一個Atom進行查詢;各個Atom再將結果返回給Matrix,Matrix將結果合併返回給client。:

資料庫代理服務中介軟體

資料庫代理服務中介軟體部署在客戶端與資料庫伺服器之間,對於客戶端而言,它就像資料庫伺服器,而對於資料庫伺服器而言,它就像客戶端。因所有資料庫服務請求都需要經過資料庫代理服務中介軟體,所以,中介軟體不僅可以記錄所有的資料庫操作,修改客戶端發過來的語句,還可以對資料庫的操作進行最佳化,實現其他如讀寫分離之類的能力

淺析分散式資料庫

該方式應用程式不需要任何修改,只需把連結指向代理伺服器,由代理伺服器訪問資料庫,該方式的優點對應用沒有侵入,缺點是效能低。

典型的資料庫代理服務中介軟體有MySQL代理、Cobar、MyCAT、TDSQL等,本文以MySQL代理為例,介紹客戶端程式庫方式的資料庫訪問中介軟體的工作原理。

MySQL代理是MySQL官方提供的MySQL資料庫代理服務中介軟體,其資料查詢過程如下:

1、 資料庫代理服務中介軟體收到客戶端傳送的SQL查詢語句;

2、 代理服務中介軟體對SQL語句進行解析,得到要查詢的相關資訊,如表名CUSTOMER;

3、 代理服務中介軟體查詢配置資訊,獲得表CUSTOMER的儲存位置資訊,如資料庫A、B、C;

4、 同時根據解析的情況進行判斷,如資料庫A、B、C上都可能存在需要查詢的資料,則將語句同時傳送給資料庫A、B、C。此時,如有必要,還可以對SQL語句進行修改。

5、 資料庫A、B、C執行收到的SQL語句,然後將查詢結果傳送給資料庫中介軟體。

6、 中介軟體收到資料庫A、B、C的結果後,將所有的結果彙總起來,根據查詢語句的要求,將結果進行合併。

7、 中介軟體將最後的結果返回給客戶端

不同的資料庫代理服務中介軟體,根據其支援的協議,可代理的資料庫不同,如Cobar只能代理MySQL資料庫,而MyCAT除了可代理MySQL外,還可代理其他關係型資料庫嗎,如Oracle、SQL Server等。

劃分為 SQL、事務和分散式 KV 層

分散式 KV 表示儲存,SQL 和事務表示計算,儲存和計算採用松耦合設計,這種方案能夠解決可擴充套件的 SQL 處理問題,但每次操作都涉及到計算和儲存之間的遠端訪問,且事務相關元資料儲存到分散式 KV 表格中,增加了事務處理的額外開銷,犧牲了效能。

第二種最經典的代表是開源的TiDB資料庫。下面是開源的TiKV,上面是SQL層,儲存用KV,這樣一來scale out的解決方案就會很方便。同樣經典的就是TiDB模仿的物件,谷歌的Spanner,下面用的是BigTable上面再構建起Spanner層。

這種做法最大的好處就是scale out做起來很好。最大的壞處,就是單節點效能不行。客戶是不是願意犧牲單節點效能,從而獲得更好的scale out,我想這個問題見仁見智。每個人可能會有不同的觀點。

以TiDB為例簡單介紹一下這種架構:

淺析分散式資料庫

TiDB Server

:SQL 層,對外暴露 MySQL 協議的連線 endpoint,負責接受客戶端的連線,執行 SQL 解析和最佳化,最終生成分散式執行計劃。TiDB 層本身是無狀態的,實踐中可以啟動多個 TiDB 例項,透過負載均衡元件(如 LVS、HAProxy 或 F5)對外提供統一的接入地址,客戶端的連線可以均勻地分攤在多個 TiDB 例項上以達到負載均衡的效果。TiDB Server 本身並不儲存資料,只是解析 SQL,將實際的資料讀取請求轉發給底層的儲存節點 TiKV(或 TiFlash)。

PD (Placement Driver) Server

:整個 TiDB 叢集的元資訊管理模組,負責儲存每個 TiKV 節點實時的資料分佈情況和叢集的整體拓撲結構,提供 TiDB Dashboard 管控介面,併為分散式事務分配事務 ID。PD 不僅儲存元資訊,同時還會根據 TiKV 節點實時上報的資料分佈狀態,下發資料排程命令給具體的 TiKV 節點,可以說是整個叢集的“大腦”。此外,PD 本身也是由至少 3 個節點構成,擁有高可用的能力。建議部署奇數個 PD 節點。

儲存節點

TiKV Server

:負責儲存資料,從外部看 TiKV 是一個分散式的提供事務的 Key-Value 儲存引擎。儲存資料的基本單位是 Region,每個 Region 負責儲存一個 Key Range(從 StartKey 到 EndKey 的左閉右開區間)的資料,每個 TiKV 節點會負責多個 Region。TiKV 的 API 在 KV 鍵值對層面提供對分散式事務的原生支援,預設提供了 SI (Snapshot Isolation) 的隔離級別,這也是 TiDB 在 SQL 層面支援分散式事務的核心。TiDB 的 SQL 層做完 SQL 解析後,會將 SQL 的執行計劃轉換為對 TiKV API 的實際呼叫。所以,資料都儲存在 TiKV 中。另外,TiKV 中的資料都會自動維護多副本(預設為三副本),天然支援高可用和自動故障轉移。

TiFlash

:TiFlash 是一類特殊的儲存節點。和普通 TiKV 節點不一樣的是,在 TiFlash 內部,資料是以列式的形式進行儲存,主要的功能是為分析型的場景加速。

緊耦合設計

SQL、事務和 KV 表示計算,KV 層資料塊依賴的分散式檔案系統表示儲存。這種方案來自於經典資料庫的 IOE 架構,SQL、事務和 KV 層表示計算,底層依賴 EMC 的 SAN 儲存。Amazon Aurora 借鑑了經典資料庫的儲存計算分離架構,並在雲原生環境下發揚光大,成為最終的事實標準。

為了追求極致效能,我認為,企業級分散式資料庫應該向經典資料庫學習原生儲存計算分離技術,我推薦這種方案。

但是最開始的Aurora版,底下是一個大磁碟,上面是計算層。這個大磁碟同時具備了Log merge的能力,這樣的做法效能是不是可以做到單節點極致其實也存疑。

至於阿里巴巴另外一個數據庫PolarDB,下面一個大磁碟接上單機MySQL,根本就沒辦法談Scale out的事情了。PolarDB-X倒是更可能Scale out。但是早年的PolarDB-X上面三節點備份,下面檔案系統再做三備份,一個記錄寫9份的騷操作,我沒有在第二個資料庫裡見過。至於現在PolarDB-X演化的怎麼樣了,我也不好判斷。

OceanBase 資料庫採用 Shared-Nothing 架構,各個節點之間完全對等,每個節點都有自己的 SQL 引擎、儲存引擎、事務引擎,執行在普通PC伺服器組成的叢集之上,具備高可擴充套件性、高可用性、高效能、低成本、與主流資料庫高相容等核心特性。

下圖是OceanBase的整體架構:

OceanBase 資料庫的一個叢集由若干個節點組成。這些節點分屬於若干個可用區(Zone),每個節點屬於一個可用區。可用區是一個邏輯概念,表示叢集內具有相似硬體可用性的一組節點,它在不同的部署模式下代表不同的含義。例如,當整個叢集部署在同一個資料中心(IDC)內的時候,一個可用區的節點可以屬於同一個機架,同一個交換機等。當叢集分佈在多個數據中心的時候,每個可用區可以對應於一個數據中心。每個可用區具有 IDC 和地域(Region)兩個屬性,描述該可用區所在的 IDC 及 IDC 所屬的地域。一般地,地域指 IDC 所在的城市。可用區的 IDC 和 Region 屬性需要反映部署時候的實際情況,以便叢集內的自動容災處理和最佳化策略能更好地工作。

在 OceanBase 資料庫中,一個表的資料可以按照某種劃分規則水平拆分為多個分片,每個分片叫做一個表分割槽,簡稱分割槽(Partition)。某行資料屬於且只屬於一個分割槽。分割槽的規則由使用者在建表的時候指定,包括hash、range、list等型別的分割槽,還支援二級分割槽。例如,交易庫中的訂單表,可以先按照使用者 ID 劃分為若干一級分割槽,再按照月份把每個一級分區劃分為若干二級分割槽。對於二級分割槽表,第二級的每個子分割槽是一個物理分割槽,而第一級分割槽只是邏輯概念。一個表的若干個分割槽可以分佈在一個可用區內的多個節點上。每個物理分割槽有一個用於儲存資料的儲存層物件,叫做 Tablet ,用於儲存有序的資料記錄。

當用戶對 tablet 中記錄進行修改的時候,為了保證資料持久化,需要記錄重做日誌(REDO)到 Tablet 對應的日誌流(Log Stream)裡。每個日誌流服務了其所在節點上的多個 tablet。為了能夠保護資料,並在節點發生故障的時候不中斷服務,每個日誌流及其所屬的 Tablet 有多個副本。一般來說,多個副本分散在多個不同的可用區裡。多個副本中有且只有一個副本接受修改操作,叫做主副本(Leader),其他副本叫做從副本(Follower)。主從副本之間透過基於 Multi-Paxos 的分散式共識協議實現了副本之間資料的一致性。當主副本所在節點發生故障的時候,一個從副本會被選舉為新的主副本並繼續提供服務。

在叢集的每個節點上會執行一個叫做 observer 的服務程序,它內部包含多個作業系統執行緒。節點的功能都是對等的。每個服務負責自己所在節點上分割槽資料的存取,也負責路由到本機的 SQL 語句的解析和執行。這些服務程序之間透過 TCP/IP 協議進行通訊。同時,每個服務會監聽來自外部應用的連線請求,建立連線和資料庫會話,並提供資料庫服務。

為了簡化大規模部署多個業務資料庫的管理並降低資源成本,OceanBase 資料庫提供了獨特的多租戶特性。在一個 OceanBase 叢集內,可以建立很多個互相之間隔離的資料庫“例項”,叫做一個租戶。從應用程式的視角來看,每個租戶是一個獨立的資料庫。不僅如此,每個租戶可以選擇 MySQL 或 Oracle 相容模式。應用連線到 MySQL 租戶後,可以在租戶下建立使用者、database,與一個獨立的 MySQL 庫的使用體驗是一樣的。同樣的,應用連線到 Oracle 租戶後,可以在租戶下建立 schema、管理角色等,與一個獨立的 Oracle 庫的使用體驗是一樣的。一個新的叢集初始化之後,就會存在一個特殊的名為 sys 的租戶,叫做系統租戶。系統租戶中儲存了叢集的元資料,是一個 MySQL 相容模式的租戶。

為了隔離租戶的資源,每個 observer 程序內可以有多個屬於不同租戶的虛擬容器,叫做資源單元(UNIT)。每個租戶在多個節點上的資源單元組成一個資源池。資源單元包括 CPU 和記憶體資源。

為了使 OceanBase 資料庫對應用程式遮蔽內部分割槽和副本分佈等細節,使應用訪問分散式資料庫像訪問單機資料庫一樣簡單,我們提供了 obproxy 代理服務。應用程式並不會直接與 OBServer 建立連線,而是連線obproxy,然後由 obproxy 轉發 SQL 請求到合適的 OBServer 節點。obproxy 是無狀態的服務,多個 obproxy 節點透過網路負載均衡(SLB)對應用提供統一的網路地址。

HTAP

HTAP並不是什麼新概念,自從2014年,Gartner提出 HTAP(Hybrid Transaction / Analytical Processing,混合事務分析處理)概念之後,HTAP就一直被熱炒,因為確實有場景支援,所以,也備受關注,因此,號稱HTAP的資料庫產品也越來越多。

但什麼才是真正的HTAP?是近年來特別火的話題,並不是具有同時處理TP 和AP的能力,就是HTAP資料庫。

目前,比較主流的觀點是,真正的HTAP,是高度融合的一個系統。這裡需要注意,高度融合的一個系統,不是把兩個不同的系統拼接在一起形成所謂的一個系統。這並不符合“一份資料”的要求,本質上還是使用兩個系統,成本必然大幅上升,並且系統後續的開發和運維,也會有各種問題。另外,這種系統是無法保證對事務的支援能力、資料實效性的。

真正的HTAP,一定是基於一體化架構。當然,這種架構在技術上實現難度更大。但對事務的支援能力和資料的實效性等方面都能提供更好的保證。

HTAP 實現通常有如下兩種做法:

淺析分散式資料庫

第一種是主備庫物理隔離,主庫做 OLTP,備庫做 OLAP,主備之間透過 redo 日誌做同步,備庫與主庫之間有一定的延遲。第二種是在同一套引擎實現 OLTP 和 OLAP 混合負載,區分 OLTP 和 OLAP 請求所在的資源組,對資源組進行邏輯隔離。

第一種方案實現相對簡單,但由於產生了更多資料冗餘,價效比較低;第二種方案實現相對複雜,但採用一體化設計,價效比更高。第二種方案來自經典資料庫,例如 Oracle、SQL Server。

我認為,企業級分散式資料庫應該學習 HTAP 技術,採用第二種方案。

經典資料庫

經典資料庫,例如 Oracle 資料庫或者 SQL Server 資料庫,其實都是在一套引擎下支援 HTAP 混合負載,這種方式做得更精細,價效比更高,且經典資料庫透過類似 Cgroup 等技術已經解決了 TP(Transactional Processing) 和 AP(Analytical Processing)隔離的問題。OceanBase 資料庫在技術上可以更多地借鑑發展了五十多年的經典關係資料庫。

TiDB

在早先版本的TiDB裡面,Raft增加了單純的listener,作為備庫,備庫是列式的儲存。有別於基於KV的OLTP workload的儲存。

TiDB早期的AP引擎是魔改ClickHouse得來的。主要利用列存進行AP處理。但是也可以在有必要的時候從行存中讀取。總體來說,還是兩套引擎。TiDB 5。0版本,據說引入了類似GreenPlum的MPP架構,使得其對OLAP的處理更上一層樓。TiDB 是計算和儲存分離的架構,底層的儲存是多副本機制,可以把其中一些副本轉換成列式儲存的副本。OLAP 的請求可以直接打到列式的副本上,也就是 TiFlash 的副本來提供高效能列式的分析服務,做到了同一份資料既可以做實時的交易又做實時的分析,這是 TiDB 在架構層面的巨大創新和突破。

TiDB 的理論基礎來源於 2013 年 Google 釋出的 Spanner / F1 論文 ,以及 2014 年 Stanford 工業級分散式一致性協議演算法 Raft 論文。在架構上,TiDB 將計算和儲存層進行高度的抽象和分離,對混合負載的場景透過 IO 優先順序佇列、智慧副本排程、行列混合儲存等技術,使 HTAP 變為可能。

參考 Google Spanner / F1 的設計,TiDB 整體架構分為上下兩層:負責計算的 TiDB Server 和 負責儲存的 TiKV Server,二者由叢集管理模組 PD Server 排程和管理。

TiDB Server 對應於 Google F1, 是一層無狀態的 SQL Layer ,其本身並不儲存資料,只負責計算。在接收到 SQL 請求後,該計算層會透過 PD Server 找到儲存計算所需資料的 TiKV 地址,然後與 TiKV Server 互動獲取資料,最終返回結果。在水平擴充套件方面,隨著業務的增長,使用者可以簡單地新增 TiDB Server 節點,提高資料庫整體的處理能力和吞吐。

作為整個叢集的管理模組,PD(Placement Driver ) 主要工作有三類:一是儲存叢集的元資訊;二是對 TiKV 叢集進行排程和負載均衡,如資料的遷移、Raft group leader 的遷移等;三是分配全域性唯一且遞增的事務 ID。

TiKV Server 是一個分散式 Key Value 資料庫,對應於 Google Spanner ,支援彈性水平擴充套件。不同於 HBase 或者 BigTable 那樣依賴底層的分散式檔案系統,TiKV Server 在效能和靈活性上更好,這對於線上業務來說非常重要。隨著資料量的增長,使用者可以部署更多的 TiKV Server 節點解決資料 Scale 的問題。PD 模組則會在 TiKV 節點之間以 Region 為單位做排程,將部分資料遷移到新加的節點上。

OceanBase

真正的 HTAP 是在 OLTP 資料庫的基礎上擴充套件 OLAP 的能力。經典的 OLTP 資料庫,無論是開源的 MySQL,還是商業資料庫 Oracle/SQL Server,都採用集中式架構,底層儲存引擎是面向磁碟設計的 B+ 樹。這種方案是為小資料量的實時事務處理量身定製的,讀寫效能很好但相比 LSM Tree 等新型資料結構儲存成本更高。以 OceanBase 為例,底層採用最佳化過的 LSM Tree 儲存引擎,在支付寶所有業務完全替換 Oracle/MySQL,儲存成本只有原來 B+ 樹方案的 1/3 左右。

經典資料庫,比如 Oracle 或者 SQL Server,其實都是在一套引擎下支援 HTAP 混合負載,這種方式做得更精細,價效比更高,且經典資料庫透過類似 cgroup 等技術已經解決了 TP 和 AP 隔離的問題。OceanBase 技術上可以更多借鑑發展了五十多年的經典關係資料庫。

OceanBase 的HTAP採用的是無共享 MPP 架構,OceanBase 資料庫自創的分散式計算引擎,能夠讓系統中多個計算節點同時執行 OLTP 型別的應用和複雜的 OLAP 型別的應用,真正實現了用一套計算引擎同時支援混合負載的能力,讓使用者透過一套系統解決 80% 的問題,充分利用客戶的計算資源,節省客戶購買額外的硬體資源、軟體授權帶來的成本。

淺析分散式資料庫

在 SQL 層,借鑑企業級資料庫,例如 Oracle 的 HTAP 設計,在同一套引擎同時支援 OLTP 和 OLAP 混合負載;在事務層,實現了支援自動容錯的分散式事務和多版本併發控制,保證與經典集中式資料庫對標的強一致 ACID;在儲存層,透過分割槽級 Paxos 日誌同步實現高可用和自動擴充套件,支援靈活的儲存架構,包括本地儲存,以及儲存計算分離。

OceanBase的AP 主要採用行列混合儲存、編譯執行、向量引擎、基於代價的查詢改寫和最佳化等技術,再加上 OceanBase 的可擴充套件性很好,OceanBase 在 AP 領域裡面的實時分析部分是業界領先的。當然,對於離線大資料處理,Spark 等大資料方案可能是更合適的選擇。

經典資料庫透過行列混合儲存支援 HTAP 混合負載,將一個表格劃分為行組(row group),每個行組內按列儲存,在行存和列存之間尋找一個平衡點。

每個行組都是一個壓縮單位,支援智慧索引和高效壓縮,例如在行組記憶體儲 count、sum 等索引元資料。另外,Oracle 和 SQL Server 都支援 In Memory 列存,OLTP 查詢訪問磁碟中的行存表,並在記憶體中額外儲存一份列存表專門用於 OLAP 查詢。HTAP 往往指的是同時支援 OLTP 業務和實時 OLAP 業務,這裡需要避免走極端。

由於 HTAP 採用行列混合儲存,對於純粹的離線 OLAP 業務,效能可能不如更加專用的 OLAP 和大資料系統。HTAP 的價值在於更加簡單通用,對於絕大部分中等規模的客戶,資料量不會特別大,只需要一套系統即可。當然,對於超大型網際網路企業,一套系統肯定是不夠的,需要部署多套系統。我們既要不斷探尋更加通用的 HTAP 方案,又要避免 one size fit all,最終的方案很可能是 one size fit a bunch。

HTAP 的另外一個技術挑戰是資源隔離。

偷懶的做法是多副本做物理隔離,主副本做 OLTP 查詢,備副本做 OLAP 查詢。另外一種是經典資料庫中採用的資源組方案,無論是 Oracle 還是 SQL Server,都支援定義不同的資源組,並限制每個資源組可以使用的物理資源。資源組的隔離可以透過類似 SQL Server 資料庫內的 SQLVM 來實現,也可以透過作業系統底層的 cgroup 機制來實現。

資料庫業務往往有三類使用者:OLTP 使用者做線上交易,Batch 使用者做批處理,DSS 使用者做報表,透過資源組限制每類使用者可使用的 CPU、IO、網路資源,從而避免 OLTP 業務受影響。Oracle 最新版本的多租戶隔離就是採用cgroup 機制,OceanBase 也採用了類似的機制,實際效果都很不錯。

相比 MySQL 等開源資料庫,經典商業資料庫的最大價值在於能夠處理複雜查詢和混合負載,企業級分散式資料庫應該向經典資料庫學習 SQL 最佳化和 SQL 執行技術。

淺析分散式資料庫

經典資料庫採用基於代價的最佳化,且支援基於代價的改寫,很多分散式資料庫的最佳化器依賴開源的 Calcite 框架,由於通用框架的限制,很難做到基於代價的改寫,複雜查詢的支援能力有限。Oracle 最佳化器分成兩個階段:第一個階段序列最佳化,第二個階段並行最佳化,對分散式和並行場景的考慮相對較少,企業級分散式資料庫可以在 Oracle 最佳化器的基礎上做創新,增強分散式和並行能力,支援一階段最佳化,在代價模型中同時考慮單機和分散式代價。

經典資料庫的 SQL 執行能力也非常出色,無論是 TPC-H、TPC-DS 等 benchmark,還是真實的業務場景,單核執行效能都非常好。企業級分散式資料庫應該學習經典資料庫的 SQL 執行技術,包括編譯執行、向量執行、 push 模型,SIMD 處理、基於結構化資料的強型別系統等等。

PolarDB

PolarDB本身的AP能力很一般。PolarDB-X的最新版本,搞的有點像GreenPlum那樣的MPP架構,但是從已經知道的細節來看,我也無法分辨這OLTP/OLAP是一套引擎還是兩套引擎,對於PolarDB的HTAP實現不是很清楚。

Paxos or Raft

Paxos

所謂的 Paxos 演算法,是為了解決來自客戶端的值被髮送到叢集中的任意一點,而後叢集中的所有節點為該值達成共識的一種協調演算法。同時這個值伴隨一個版本號,可以保證訊息是有順序的,該順序在叢集中任何一點都是一致的。

基本的 Paxos 演算法非常簡單,它由三個角色組成。

Proposer:Proposer 可以有多個,Proposer 提出議案(value)。所謂 value,可以是任何操作,比如“設定某個變數的值為 value”。不同的 Proposer 可以提出不同的 value。但對同一輪 Paxos 過程,最多隻有一個 value 被批准。

Acceptor:Acceptor 有 N 個,Proposer 提出的 value 必須獲得 Quorum 的 Acceptor 批准後才能透過。Acceptor 之間完全對等獨立。

Learner:上面提到只要 Quorum 的 Accpetor 透過即可獲得透過,那麼 Learner 角色的目的就是把透過的確定性取值同步給其他未確定的 Acceptor。

這三個角色其實已經描述了一個值被提交的整個過程。其實基本的 Paxos 只是理論模型,因為在真實場景下,我們需要處理許多連續的值,並且這些值都是併發的。如果完全執行上面描述的過程,那效能消耗是任何生產系統都無法承受的,因此我們一般使用的是 Multi-Paxos。

Multi-Paxos 可以併發執行多個 Paxos 協議,它最佳化的重點是把 Propose 階段進行了合併,這就引入了一個 Leader 的角色,也就是領導節點。而後讀寫全部由 Leader 處理,同時這裡與 ZAB 類似,Leader 也有任期的概念,Leader 與其他節點之間也用心跳進行互相探活。是不是感覺有那個味道了?後面我就會比較兩者的異同。

另外 Multi-Paxos 引入了兩個重要的概念:replicated log 和 state snapshot。

replicated log:值被提交後寫入到日誌中。這種日誌結構除了提供持久化儲存外,更重要的是保證了訊息儲存的順序性。而 Paxos 演算法的目標是保證每個節點該日誌內容的強一致性。

state snapshot:由於日誌結構儲存了所有值,隨著時間推移,日誌會越來越大。故演算法實現了一種狀態快照,可以儲存最新的日誌訊息。當快照生成後,我們就可以安全刪除快照之前的日誌了。

淺析分散式資料庫

2014 年,OceanBase 將 Paxos 協議引入到資料庫,首次在金融核心系統做到 RPO=0,也正是依靠這項技術,成為支付寶的最終選擇。基於 Paxos 協議,OceanBase 繼續研發了同城三機房、兩地三中心、三地五中心等多種靈活的容災部署方案。

經典集中式資料庫往往採用主備同步方案,有兩種同步模式:第一種是強同步,每個事務操作都需要強同步到備機才可以應答使用者,這種方式能夠做到伺服器故障不丟資料,但必須停服務,無法保證可用性;另外一種是非同步,每個事務操作只需要在主機成功就可以應答使用者,這種方式能夠做到高可用,但主庫和備庫之間資料不一致,備庫切換為主庫之後會丟資料。

強一致和高可用作為資料庫最重要的兩個特性,在主備同步模式下,魚和熊掌不可兼得。Paxos 是一種基於多數派的分散式投票協議,每個事務需要成功寫入超過一半伺服器才可以應答使用者。俗話說得好,“三個臭皮匠頂過一個諸葛亮”,假設總共有 3 臺伺服器,每個事務操作要求至少在 2 臺伺服器上成功,無論任何一臺伺服器發生故障,系統中還有 1 臺包含了全部資料的伺服器能夠正常工作,從而做到完全不丟資料,並在 30 秒之內選出新的主庫恢復服務,RPO 等於 0,RTO 小於 30 秒。所有保證 RPO=0 的協議都是 Paxos 協議或者 Paxos 協議的變種,Raft 協議就是其中之一。

Raft

Raft 可以看成是 Multi-Paxos 的改進演算法,因為其作者曾在斯坦福大學做過關於 Raft 與 Multi-Paxos 的比較演講,因此我們可以將它們看作一類演算法。

Raft 演算法可以說是目前非常不錯的的分散式共識演算法,包括 TiDB、FaunaDB、Redis 等都使用了這種技術。原因是 Multi-Paxos 沒有具體的實現細節,雖然它給了開發者想象空間,但共識演算法一般居於核心位置,一旦存在潛在問題必然帶給系統災難性的後果。而 Raft 演算法給出了大量的實現細節,且處理方式相比於 Multi-Paxos 有兩點優勢。

傳送的請求的是連續的,也就是說 Raft 的寫日誌操作必須是連續的;而 Multi-Paxos 可以併發修改日誌,這也體現了“Multi”的特點。

選主必須是最新、最全的日誌節點才可以當選,這一點與 ZAB 演算法有相同的原則;而 Multi-Paxo 是隨機的。因此 Raft 可以看成是簡化版本的 Multi-Paxos,正是這個簡化,造就了 Raft 的流行。

Multi-Paxos 隨機性使得沒有一個節點有完整的最新的資料,因此其恢復流程非常複雜,需要同步節點間的歷史記錄;而 Raft 可以很容易地找到最新節點,從而加快恢復速度。當然亂序提交和日誌的不連續也有好處,那就是寫入併發效能會大大提高,從而提高吞吐量。所以這兩個特性並不是缺點,而是權衡利弊的結果。當然 TiKV 在使用 Raft 的時候採用了多 RaftGroup 的模式,提高了單 Raft 結構的併發度,這可以被看作是向 Multi-Paxos 的一種借鑑。

同時 Raft 和 Multi-Paxos 都使用了任期形式的 Leader。好處是效能很高,缺點是在切主的時候會拒絕服務,造成可用性下降。因此一般我們認為共識服務是 CP 類服務(CAP 理論)。但是有些團隊為了提高可用性 ,轉而採用基礎的 Paxos 演算法,比如微信的 PaxosStore 都是用了每輪一個單獨的 Paxos 這種策略。

以上兩點改進使 Raft 更好地落地,可以說目前最新資料庫幾乎都在使用該演算法。想了解演算法更多細節,請參考https://raft。github。io/。你從中不僅能學習到演算法細節,更重要的是可以看到很多已經完成的實現,結合程式碼學習能為你帶來更深刻的印象。

如何選擇

Raft 協議是 Paxos 協議的一種簡化,原理是在 Paxos 協議基礎上增加了一個限制,要求按順序投票,也就意味著資料庫 redo 日誌順序同步。Raft 協議的好處是實現簡單,但每個分割槽只能順序同步,併發同步效能較差,在弱網環境下事務卡頓現象機率較高;Paxos 協議支援亂序同步,雖然實現更加複雜,但併發同步效能好,在弱網環境下事務卡頓現象機率較低。Raft 類系統發明了一個概念叫:Multi-Raft,指的是每個分片執行一個 Raft 協議。這個概念和經典的 Multi-Paxos 是不對等的,Multi-Paxos 指的是一個分片內做併發,Multi-Raft 指的是多個分片之間做併發,如果單個分片寫入量很大,仍然是會產生效能瓶頸的。

Paxos 和 Raft 兩種協議都是合理的做法,據悉目前國內有兩個資料庫產品使用了Raft,一個是阿里雲的PolarDB。它用了自己改進的Paralle Raft協議。另外一個是TiDB。Paxos也是頂尖網際網路公司在大規模分散式儲存系統的主流做法,包括 Google Spanner,Microsoft Azure,Amazon DynamoDB,OceanBase 等等。

這當然有歷史原因,Raft出來的比較晚。但是也有現實的原因,單從效能方面來說Paxos是領先Raft的,Paxos協議的實現有很多很難的地方,一不小心就栽坑。我自己親自經歷的就看到過幾個大Bug,導致資料丟失,都是Paxos協議實現細節上不對導致的。要實現一個好的Raft,估計難度上會相對容易一些。這當然也會成為某些專案的選擇。

儲存引擎

儲存引擎的特點千差萬別,各具特色。但總體上我們可以透過三個變數來描述它們的行為:快取的使用方式,資料是可變的還是不可變的,儲存的資料是有順序的還是沒有順序的。

快取形式

快取是說儲存引擎在資料寫入的時候,首先將它們寫入到記憶體的一個片段,目的是進行資料匯聚,而後再寫入磁碟中。這個小片段由一系列塊組成,塊是寫入磁碟的最小單位。理想狀態是寫入磁碟的塊是滿塊,這樣的效率最高。

大部分儲存引擎都會使用到快取。但使用它的方式卻很不相同,比如我將要介紹的 WiredTiger 快取 B 樹節點,用記憶體來抵消隨機讀寫的效能問題。而我們介紹的 LSM 樹是用快取構建一個有順序的不可變結構。故使用快取的模式是衡量儲存引擎的一個重要指標。

可變/不可變資料

儲存的資料是可變的還是不可變的,這是判斷儲存引擎特點的另一個維度。不可變性一般都是以追加日誌的形式存在的,其特點是寫入高效;而可變資料,以經典 B 樹為代表,強調的是讀取效能。故一般認為可變性是區分 B 樹與 LSM 樹的重要指標。但 BW-Tree 這種 B 樹的變種結構雖然結構上吸收了 B 樹的特點,但資料檔案是不可變的。

當然不可變資料並不是說資料一直是不變的,而是強調了是否在最影響效能的寫入場景中是否可變。LSM 樹的合併操作,就是在不阻塞讀寫的情況下,進行資料檔案的合併與分割操作,在此過程中一些資料會被刪除。

排序

最後一個變數就是資料儲存的時候是否進行排序。排序的好處是對範圍掃描非常友好,可以實現 between 類的資料操作。同時範圍掃描也是實現二級索引、資料分類等特性的有效武器。如本模組介紹的 LSM 樹和 B+ 樹都是支援資料排序的。

而不排序一般是一種對於寫入的最佳化。可以想到,如果資料是按照寫入的順序直接儲存在磁碟上,不需要進行重排序,那麼其寫入效能會很好,下面我們要介紹的 WiscKey 和 Bitcask 的寫入都是直接追加到檔案末尾,而不進行排序的。

以上就是評估儲存引擎特點的三個變數,我這裡將它們稱為黃金三角。因為它們是互相獨立的,彼此並不重疊,故可以方便地評估儲存引擎的特點。下面我們就試著使用這組黃金三角來評估目前流行的儲存引擎的特點。

B 樹類

上文我們提到過評估儲存引擎的一個重要指標就是資料是否可以被修改,而 B 樹就是可以修改類儲存引擎比較典型的一個代表。它是目前的分散式資料庫,乃至於一般資料庫最常採用的資料結構。它是為了解決搜尋樹(BST)等結構在 HDD 磁碟上效能差而產生的,結構特點是高度很低,寬度很寬。檢索的時候從上到下查詢次數較少,甚至如 B+ 樹那樣,可以完全把非葉子節點載入到記憶體中,從而使查詢最多隻進行一次磁碟操作。

下面讓我介紹幾種典型的 B 樹結構的儲存引擎。

InnoDB

InnoDB 是目前 MySQL 的預設儲存引擎,同時也是 MariaDB 10。2 之後的預設儲存引擎。

根據上文的評估指標看,它的 B+ 樹節點是可變的,且葉子節點儲存的資料是經過排序的。同時由於資料的持續寫入,在高度不變的情況下,這個 B+ 樹一定會橫向發展,從而使原有的一個節點分裂為多個節點。而 InnoDB 使用快取的模式就是:為這種分裂預留一部分記憶體頁面,用來容納可能的節點分裂。

這種預留的空間其實就是一種浪費,是空間放大的一種表現。用 RUM 假設來解釋,InnoDB 這種結構是犧牲了空間來獲取對於讀寫的最佳化。

在事務層面,InnoDB 實現了完整的隔離級別,透過 MVCC 機制配合各種悲觀鎖機制來實現不同級別的隔離性。

WiredTiger

WiredTiger 是 MongoDB 預設的儲存引擎。它解決了原有 MongoDB 必須將大部分資料放在記憶體中,當記憶體出現壓力後,資料庫效能急劇下降的問題。

它採用的是 B 樹結構,而不是 InnoDB 的 B+ 樹結構。這個原因主要是 MongoDB 是文件型資料庫,採用內聚的形式儲存資料(你可以理解為在關係型資料庫上增加了擴充套件列)。故這種資料庫很少進行 join 操作,不需要範圍掃描且一次訪問就可以獲得全部資料。而 B 樹每個層級上都有資料,雖然查詢效能不穩定,但總體平均效能是要好於 B+ 樹的。

故 WiredTiger 首先是可變資料結構,同時由於不進行順序掃描操作,資料也不是排序的。那麼它是如何運用快取的呢?這個部分與 InnoDB 就有區別了。

在快取中每個樹節點上,都配合一個更新緩衝,是用跳錶實現的。當進行插入和更新操作時,這些資料寫入緩衝內,而不直接修改節點。這樣做的好處是,跳錶這種結構不需要預留額外的空間,且併發效能較好。在刷盤時,跳錶內的資料和節點頁面一起被合併到磁碟上。

由此可見,WiredTiger 犧牲了一定的查詢效能來換取空間利用率和寫入效能。因為查詢的時候出來讀取頁面資料外,還要合併跳錶內的資料後才能獲取最新的資料。

BW-Tree

BW-Tree 是微軟的 Azure Cosmos DB 背後的主要技術棧。它其實透過軟體與硬體結合來實現高效能的類 B 樹結構,硬體部分的最佳化使用 Llama 儲存系統,有興趣的話你可以自行搜尋學習。我們重點關注資料結構方面的最佳化。

BW-Tree 為每個節點配置了一個頁面 ID,而後該節點的所有操作被轉換為如 LSM 樹那樣的順序寫過程,也就是寫入和刪除操作都是透過日誌操作來完成的。採用這種結構很好地解決了 B 樹的寫放大和空間放大問題。同時由於存在多個小的日誌,併發性也得到了改善。

刷盤時,從日誌刷入磁碟,將隨機寫變為了順序寫,同樣提高了刷盤效率。我們會發現,BW-Tree 也如 LSM 樹一樣存在讀放大問題,即查詢時需要將基礎資料與日誌資料進行合併。而且如果日誌太長,會導致讀取緩慢。而此時 Cosmos 採用了一種硬體的解決方案,它會感知同一個日誌檔案中需要進行合併的部分,將它們安排在同一個處理節點,從而加快日誌的收斂過程。

以上就是典型的三種 B 樹類的儲存引擎,它們各具特色,對於同一個問題的最佳化方式也帶給我們很多啟發。

LSM 類

LSM是典型的不可變資料結構,使用快取也是透過將隨機寫轉為順序寫來實現的。

我們在說 LSM 樹時介紹了它儲存的資料是有順序的,其實目前有兩種無順序的結構也越來越受到重視。

經典儲存

經典的 LSM 實現有 LeveledDB,和在其基礎之上發展出來的 RocksDB。它們的特點我們之前有介紹過,也就是使用快取來將隨機寫轉換為順序寫,而後生成排序且不可變的資料。它對寫入和空間友好,但是犧牲了讀取效能。

Bitcask

Bitcask 是分散式鍵值資料庫 Riak 的一種儲存引擎,它也是一種典型的無順序儲存結構。與前面介紹的典型 LSM 樹有本質上的不同,它沒有記憶體表結構,也就是它根本不進行快取而是直接將資料寫到資料檔案之中。

可以看到,其寫入是非常高效的,記憶體佔用也很小。但是如何查詢這種“堆”結構的資料呢?答案是在記憶體中有一個叫作 Keydir 的結構儲存了指向資料最新版本的引用,舊資料依然在資料檔案中,但是沒有被 Keydir 引用,最終就會被垃圾收集器刪除掉。Keydir 實際上是一個雜湊表,在資料庫啟動時,從資料檔案中構建出來。

這種查詢很明顯改善了 LSM 樹的讀放大問題,因為每條資料只有一個磁碟檔案引用,且沒有快取資料,故只需要查詢一個位置就可以將資料查詢出來。但其缺陷同樣明顯:不支援範圍查詢,且啟動時,如果資料量很大,啟動時間會比較長。

此種結構優化了寫入、空間以及對單條資料的查詢,但犧牲了範圍查詢的功能。

RocksDB

RocksDB 是由 Facebook 基於 LevelDB 開發的一款提供鍵值儲存與讀寫功能的 LSM-tree 架構引擎。使用者寫入的鍵值對會先寫入磁碟上的 WAL (Write Ahead Log),然後再寫入記憶體中的跳錶(SkipList,這部分結構又被稱作 MemTable)。LSM-tree 引擎由於將使用者的隨機修改(插入)轉化為了對 WAL 檔案的順序寫,因此具有比 B 樹類儲存引擎更高的寫吞吐。

記憶體中的資料達到一定閾值後,會刷到磁碟上生成 SST 檔案 (Sorted String Table),SST 又分為多層(預設至多 6 層),每一層的資料達到一定閾值後會挑選一部分 SST 合併到下一層,每一層的資料是上一層的 10 倍(因此 90% 的資料儲存在最後一層)。

RocksDB 允許使用者建立多個 ColumnFamily ,這些 ColumnFamily 各自擁有獨立的記憶體跳錶以及 SST 檔案,但是共享同一個 WAL 檔案,這樣的好處是可以根據應用特點為不同的 ColumnFamily 選擇不同的配置,但是又沒有增加對 WAL 的寫次數。

MPP

MPP (Massively Parallel Processing) 大規模並行處理,MPP是將任務並行的分散到多個伺服器和節點上,在每個節點上計算完成後,將各自部分的結果彙總在一起得到最終的結果(與Hadoop相似)。

MPP架構具有如下特徵:

任務並行執行

資料分散式儲存(本地化)

分散式計算

私有資源

橫向擴充套件

Shared Nothing架構

MPP並行處理的關鍵點在於將資料均勻的分佈到每一塊磁碟上,從而發揮每一塊磁碟效能,從根本上解決IO瓶頸問題。資料的均勻分佈可以充分的利用物理硬體資源,因此它也是效能調優的基礎。資料的均勻分佈很大程度上依賴於分佈鍵和具體分佈方式的選擇,常用的分佈方式有雜湊和迴圈等。

淺析分散式資料庫

MPP採用shared-Nothing的架構模型,理論上可以做到線性擴充套件來提升資料倉庫的效能。但生產實踐中,通常受限於網路收斂比、成本等因素,往往最多隻能擴充套件幾百節點。

MPP在具體架構實現上,通常有無Master和Master-Slave兩種方式,無共享Master的結構方式中,所有節點是對等的,客戶端可以透過任意的節點來載入資料,不存在效能瓶頸和單點故障風險。

基於MPP架構的資料庫(MPPDB)是一種 Shared-Nothing架構的分散式並行結構化資料庫叢集,具備高效能、高可用、高擴充套件特性,可以為超大規模資料管理提供高性價比的通用計算平臺,並廣泛地用於支撐各類資料倉庫系統、BI 系統和決策支援系統。

對於一張表中的多條記錄來說,客戶端的資料透過解析器進行解析後,將資料分發給各個處理單元進行處理,每個處理單元將接收到的記錄儲存到自己的邏輯盤中。GaussDB中解析器對應協調節點CN,處理單元對應資料節點DN。

淺析分散式資料庫

當客戶端發出查詢請求時,解析器同樣將查詢命令分發給各個處理單元,各個處理單元並行的定位到查詢的記錄並返回給解析器,解析器將各個處理單元的查詢結果合併後返回給客戶端。

對於多張表來說,每個表的記錄都會分佈在各個處理單元上,每個處理單元都會有各個表的記錄。理想情況下,記錄會均勻的分佈到各個處理單元上。

淺析分散式資料庫

如果採用的是根據分佈鍵的雜湊值來確定記錄的分佈節點,那麼分佈鍵一般在建表時指定,如果沒有指定,系統會根據一定的規則選擇預設欄位作為分佈鍵。

MPP架構下資料庫執行排序操作時,每個處理單元會先進行內部的資料排序,然後再將各處理單元資料進行多路歸併排序。

MPP架構下資料庫執行關聯操作時,如果關聯欄位是分佈鍵,那麼關聯的欄位必然分佈在相同的節點上,則在每個節點分別進行關聯,然後將結果集合並;如果關聯欄位不是分佈鍵,則需要資料在節點間進行流動再進行關聯、合併。

MPP架構下資料庫進行聚合操作,每個處理單元會進行節點內部的資料聚合,之後再將各處理單元資料進行彙總。如果聚合欄位不是分佈鍵,也會有資料流動。

MPP資料庫有對SQL的完整相容和一些事務的處理能力,對於使用者來說,在實際的使用場景中,如果資料擴充套件需求不是特別大,需要的處理節點不多,資料都是結構化的資料,習慣使用傳統的RDBMS的很多特性的場景,可以考慮MPP,例如Greenplum/Gbase等。

如何選擇分散式資料庫

如何選擇一款分散式資料庫,可以考慮如下幾點:

1。 要相容MySQL,因為本人就是MySQL重度研究與使用者,高度認可MySQL這個資料庫的架構及使用方式等(中毒已深)。相容MySQL這個要求,其實是非常高的,我們每個人都知道。只是MySQL的語法比較亂(說到程式碼實現,可能更多的是罵了),很鬆散,如果說做到了90%的相容度,那是不夠的,最好要做到100%,這能做到嗎?我想是可以的。目前像國內的OceanBase、TiDB、PolarDB都能夠相容MySQL。

2。 儲存率高,使用分散式資料庫的業務,大部分應該是儲存分析型,如果使用了分散式資料庫,還需要佔用太多的硬體資源,且儲存不了太多資料的話,那這個在成本上就非常高了,得不償失。比如OceanBase,基於LSM儲存引擎,底層有一定的壓縮機制,比傳統MySQL儲存能夠降低30%。

3。 有健全的圈子,使用中難免會碰到問題,碰到問題的時候,現在處於分散式發展的初級階段,所以社群的人比較少,而只能去求助官方,如果官方不能提供幫助(也許是沒給錢),那這樣的資料庫,可能就不具有誘惑力了,風險太大。目前國內的OceanBase、TiDB、PolarDB都已經開源,且有一定的售後支援。

4。 效能夠用,在使用了分散式資料庫之後,其實已經預設接受了降低效能要求的條件,所以我們的要求只是說,效能夠用即可,不會去和單點MySQL去比,因為沒有意義。夠用就好,當然在這方面如果足夠好,那是再好不過了。效能這塊目前國內的OceanBase、TiDB、PolarDB都不錯,尤其是OceanBase參與全球頂級的TPC-C 測試,位列全球第一,遠超第二名Oracle(OceanBase天然具備分散式,很自然可以分多個片,手動分片無限堆機器自然分數比較高,純單機的測試和Oracle比的話結果不好說)。

5。 少技術棧,這樣的需求是非常高的,因為技術棧太長,會加重運維人員的成本,並且在現在這樣人才難找更難招的情況下,這樣的願望是更迫切的。

分散式資料庫發展趨勢

企業級分散式資料庫除了做好分散式,更要堅守資料庫的初心,在功能相容性、價效比上下功夫。今天,分散式資料庫領域流行的一些技術,包括儲存計算分離、HTAP,最早都出自經典資料庫,且經典資料庫經過五十多年的實踐,透過應用驅動技術創新做得更加精細。

我認為企業級分散式資料庫是未來的大趨勢,最終能不能走到那一步,取決於我們能否站在經典資料庫這一巨人的肩膀上,深挖經典資料庫的核心技術和理念,在消化理解的基礎上做創新。企業級分散式資料庫在分散式架構上做了很多創新,包括分割槽級 Paxos 日誌同步實現高可用和可擴充套件,支援容錯的分散式事務和多版本併發控制,但對經典資料庫的核心技術理解還需要加強,例如深入理解 Oracle 和 SQL Server 等企業級資料庫的 HTAP 和儲存計算分離技術。

結合《2022 分散式資料庫發展趨勢研究報告》,分散式資料庫有如下九大發展趨勢

分散式資料庫走向原生設計

分散式資料庫架構的設計走向一體化

分散式資料庫的能力將向混合負載發展

分散式資料庫的場景將向雲化發展

分散式資料庫的高可用能力不斷在提升

分散式資料庫對資料一致性的支援將日臻完善

分散式資料庫的生態建設亟需推動

分散式資料庫需要支援異構晶片的混合部署

分散式資料庫應支援資料透明加密