CynosDB技術詳解——架構設計

本文由騰訊雲數據庫發表

前言

CynosDB是新一代分佈式數據庫,100%兼容MySQL和PostgreSQL,支持存儲彈性擴展,一主多從共享數據,性能更是超越社區原生MySQL和PostgreSQL。CynosDB採用share storage架構,其彈性擴展和高性價比的基石則是CynosDB File System(簡稱CynosFS):一款騰訊雲自研的用戶態分佈式文件系統。本文旨在從整體上講述CynosDB和CynosFS的核心架構設計。

挑戰與應對

CynosDB是公有云原生架構的,其核心思想是在資源池化的基礎上實現公有云高性價比、高可用性以及彈性擴展等諸多優勢。實現資源池化的最大技術挑戰是高效、穩定的彈性調度能力,該能力也是公有云產品高可用性的基石。計算、網絡和存儲是公有云三大類Iaas產品,前面兩類都有比較成熟的彈性調度架構,唯獨存儲產品例外。究其根本還是數據調度的代價太高。數據庫是典型的計算+存儲的產品,要做到資源池化需要:

l存儲與計算分離:這樣計算資源(主要包含CPU和內存)就可以使用現有成熟的容器、虛擬機等技術實現資源池化

l分佈式存儲:將數據分割成規格化的塊,引入分佈式調度系統,實現存儲容量和IO的彈性調度,從而實現存儲資源的池化

那麼對於數據庫產品來說,是否有現成架構可以很好的滿足以上兩個需求呢。我們發現虛擬機+分佈式塊存儲(雲盤)的方案非常合適,目前主流公有云平臺也都有非常成熟的產品。騰訊雲今年早些時候推出的MySQL基礎版以及AWS上的RDS都是基於這種架構。圖1簡單描述了這種架構:

img

圖 1

但該架構有如下不足:

l網絡IO重:可以看到就1個數據庫實例,就有大量數據需要寫到雲盤,這些數據主要包括:WAL LOG、頁數據、防止頁部分寫的Double Write或者Full Page Write。除此之外,雲盤還要將這些數據做多個備份

l主從實例不共享數據:一方面浪費了大量存儲,另一方面進一步加重了網絡IO

這些不足導致基於該架構的數據庫產品在系統吞吐能力上無法與基於物理機部署的主從架構競爭,而且延遲也受到很大的挑戰,這對OLTP類業務的影響非常明顯。同時每個數據庫實例都單獨擁有一份(實際可能是2-3份)存儲,成本也很高。

CynosDB針對這兩個不足,採用瞭如下設計:

l日誌下沉: WAL LOG從邏輯上已經包含了數據的所有變動,CynosDB將WAL LOG下沉到存儲層,數據庫實例就只需要將WAL LOG寫到存儲層,不再需要寫頁數據(頁數據以及Double Write或Full Page Write),同時這個WAL LOG也作爲RAFT協議的日誌來完成多個數據備份的同步,進一步減少了網絡IO。

l主從實例共享數據:CynosDB的主從實例共享共一份存儲數據,進一步減少了網絡IO,同時極大的減少了存儲容量

循着這兩個解決思路,我們對傳統基於雲盤的架構進行優化,就有了CynosDB如下架構:

img

圖 2

圖中組件包括:

lDB Engine:數據庫引擎,支持一主多從。

lDistributed File System:用戶態分佈式文件系統,主要提供分佈式的文件管理,負責將文件的讀寫請求翻譯爲對應的BLOCK讀寫

lLOG/BLOCK API:Storage Service提供的讀寫接口,對於讀寫請求有不同處理:

n寫請求:將修改日誌(等價於WAL LOG)通過LOG API發送到Storage Service

n讀請求:直接通過BLOCK API讀取數據

lDB Cluster Manager:負責一主多從DB集羣的HA管理。

lStorage Service:負責日誌的處理、BLOCK數據的異步回放、讀請求的多版本支持等。同時還負責將WAL LOG備份到Cold Backup Storage

lSegment(Seg):Storage Service管理數據BLOCK和日誌的最小單元(10GB),也是數據複製的實體。圖中同樣顏色的3個Seg實際存儲同一份數據,通過一致性協議(Raft)進行同步,我們叫做Segment Group(SG)。

lPool:多個SG從邏輯上構成一個連續的存儲數據BLOCK的塊設備,供上層的Distributed File System分配使用。Pool和SG是一對多的關係。

lStorage Cluster Manager:負責Storage Service以及Segment Group 的HA調度,以及維護Pool和SG的對應關係。

lCold Backup Service:接收到WAL LOG後,進行增量備份,根據增量備份,可以靈活的生成全量備份和差異備份

可以看到,上面中除了DB Engine和DB Cluster Manager外的所有模塊構成了一個與數據庫引擎無關的用戶態分佈式文件系統,我們命名爲:CynosFS

日誌下沉和異步回放

這裏CynosDB借鑑了AWS Aurora論文中日誌即數據庫的思想,將WAL LOG下沉到Storage Serivce,計算層只寫WAL LOG,而由Storage Serivce異步地將WAL LOG應用到對應的BLOCK上,在減少寫IO的同時,也是Storage Serivce提供MVCC讀能力的基礎。而MVCC是主從節點共享存儲的基礎。Storage Serivce中的數據和日誌都是以Segment爲單元進行管理的,圖3描述了寫的基本流程:

img

圖 3

1.接收到更新日誌,寫到磁盤,並行發起第2步

2.發起RAFT日誌複製

3.RAFT多數派提交成功,返回寫日誌成功

4.異步的將日誌記錄掛載到對應數據BLOCK的更新鏈上

5.合併更新鏈中的日誌記錄到數據BLOCK

6.將日誌備份到冷備系統

7.回收無用日誌

這裏第4步會對更新的數據BLOCK形成1個更新鏈,從而支持數據BLOCK多個版本的讀,進而提供MVCC能力。通過控制第5步的合併進度,就可以控制MVCC的起始窗口。

同時CynosDB還將頁面CRC等操作放到了存儲服務中,充分利用存儲服務器的CPU資源

MVCC實現

CynosFS的MVCC能力是CynosDB一主多從架構的基礎,本小節會詳細描述其運行機制。

首先我們引入一系列概念:

lMini-transaction(MTR):就像數據庫事務用來保證事務的ACID特性,MTR用來保證磁盤上頁數據的ACID特性。舉個例子,1個B+樹的插入操作,在造成節點分裂的情況下,最多會修改3個數據頁,這3個數據頁的修改需要當做1個事務來處理,這就是MTR。數據庫實例在磁盤上的數據是按照MTR爲最小單元進行更新的

lWrite Ahead Log(WAL LOG):關係數據庫常用的保證數據一致性的技術。WAL LOG是一個增量日誌流水,裏面的日誌記錄(Log Records)保存了頁面修改操作。由於CynosFS是與具體數據庫引擎無關的分佈式文件系統,其WAL LOG中記錄的是對數據BLOCK的二進制修改,是物理日誌

lLog Sequence Number(LSN):WAL LOG中每條Log Record都會有1個唯一標識的序列號,從1開始,按Log Record產生的連續單調遞增。

lConsistency Point LSN(CPL):一個MTR可能對應多個磁盤數據頁的修改,相應的會產生多條日誌記錄,這批日誌記錄中的最後1條(也就是LSN值最大的1條)標識爲CPL,這條日誌記錄對應了一個磁盤上的數據狀態,該狀態表示1個MTR的完整應用,也是MVCC的讀一致性狀態

lSegment Group Complete LSN(SGCL):1個數據庫實例的數據頁會分佈到多個SG上,因此每個SG只會持有部分日誌記錄,SCL表示該SG已經將所有小於等於SCL的日誌記錄持久化到磁盤上了。對於CynosFS來說,因爲SG採用了Raft協議,SCL就是Raft的CommitIndex。

lPool Complete LSN(PCL):表示構成該Pool的所有SG已經將小於等於PCL的日誌持久化到磁盤上。因爲LSN是連續單調遞增的,這也要等價於中間沒有空洞

lPool Consistency Point LSN(PCPL):不大於PCL的最大的CPL。邏輯上表示:已經持久化到磁盤的可提供MVCC讀一致性的點。

首先主實例對所有數據頁的修改都經過Distributed File System的轉換,形成Pool級別的日誌,並附帶連續單調遞增的LSN,分發到不同的SG。SG會在完成Raft Commmit後推進自己的SGCL並返回,主實例根據所有SG的完成情況,推進PCL和PCPL。下面我們通過2幅圖描述PCL和PCPL的推進過程:

img

圖 4

首先該Pool由2個SG構成,每個SG中有3個Segment,存儲同一份數據。當前有LSN爲1~8的日誌已經持久化到2個SG中,其中1、3、4、7在SG A上,2、5、6、8在SG B上。這8條日誌屬於3個MTR,分別是:MTR-x(1、2、3),MTR-y(4、5),MTR-z(6、7、8),其中3和5都是CPL,表示MTR-x和MTR-y對應的日誌已經全部持久化了,8不是CPL,說明MTR-z後續還有日誌記錄。按照上面的定義,此時PCL爲8,而PCPL爲5

img

圖 5

圖5中,MTR-z的1條日誌CPL9被持久化到SG A,按照定義,此時PCL和PCPL都更新到了9。

這裏主要指從實例的讀流程。從實例具有自己的內存數據,在主實例更新了數據後,需要同步更新從實例的內存數據,否則會出現不一致的情況。主實例會將日誌記錄以及最新的PCPL值推送給從實例。因此從實例的PCPL值會較主實例有所延遲。這個PCPL值決定了從實例上所有事務能讀到的最後1個MTR。同時爲了減少從實例的延遲,主實例可能不等PCPL值推進先把日誌推送到從實例,因此從實例需要保證大於本地PCPL值的那些日誌不被應用。從實例對這些更新日誌的處理方式有兩類:如果對應修改的數據頁在Buffer Pool中,則應用此日誌更新到對應數據頁,否則直接丟棄。最後從實例上的讀事務在每次訪問數據頁時(不管直接從Buffer中獲取到,還是從Storage Service獲取),因爲可能一次讀入多個頁,所以需要取當前的PCPL值爲Read Point LSN(簡稱:RPL),用RPL達成到一致性讀(因爲PRL是一個PCPL值,其一定能保證MTR的完整性)。CynosDB支持一主多從,每個從實例上都會有很多讀事務,這些讀事務都具有自己的RPL,所有這些RPL中最小的我們稱之爲Min-RPL(簡稱:MRPL),那麼在MRPL和PCPL之間可能會有多個CPL點,不同讀事務的RPL可能在這個區間的任何1個CPL點上。因此Storage Service要能支持讀取在這個區間內任意CPL點的數據,也就是MVCC。CynosDB使用數據頁+更新日誌鏈的方式來實現。

最後讓我們回到圖3,SG需要定期回收無用的日誌記錄,我們可以看到,所謂無用的日誌記錄就是其LSN值小於MRPL的那些。而大於等於MRPL的那些日誌因爲要用來支持MVCC,所以必須保留。

事務提交

一個數據庫事務由多個MTR組成,最後1個MTR的最後1條日誌的LSN(必然也是個CPL)我們稱之爲Commit LSN(簡稱:CLSN)。爲了提升效率,日誌到Storage Service的推送採用異步流水線的方式,同時按照SG進行分組並行推送。全局來看,因爲是並行的,所以可能會有亂序,但推送到某個SG上的日誌一定是按照LSN單調遞增的(不連續)。隨着PCPL的推進,一旦PCPL值大於某個事物的CLSN,該事物就可以完成Commit操作了。

崩潰恢復

首先,CynosDB會持續的保存主實例的PCPL,這個PCPL的備份值我們稱之爲:Last PCPL(簡稱:L-PCPL)。CynosDB需要保證L-PCPL值的更新足夠快,以確保那些大於L-PCPL的日誌記錄沒有被回收。在此前提下,我們通過如下步驟實現崩潰恢復:

img

圖 6

首先,新的主實例獲取到L-PCPL值爲3。該實例由2個SG組成,其中SG A中已經持久化了日誌(1、4、5),而SG B中持久化了日誌(2、3、6、8)。

img

圖 7

主實例向2個SG讀取大於3的所有日誌LSN,獲取到SG A的4、5以及SG B的6、8。這樣形成了3、4、5、6、8的日誌流。按照定義,因爲沒有LSN爲7的日誌,所以8是無效的,其次6不是CPL,所以6也是無效的,這樣就計算出了最新的PCPL值爲5。然後將新的PCPL值推送給所有的從實例。

img

圖 8

最後,主實例將新的PCPL=5推送給所有SG,以便其將大於5的所有日誌都清理掉。

經過上面三步,整個數據就回到了PCPL=5這個一致性的點。主實例和從實例就可以提供服務了。

持續備份

實際上,在CynosDB中,幾乎不需要全量的備份操作,只要保證SG的WAL LOG在回收前,被增量保存到了冷備系統中就行了。因爲邏輯上WAL LOG是包含了所有數據的修改操作,我們可以使用增量的WAL LOG,按照客戶需求,生成全量備份和差異備份。而這些操作都可以是由另一套獨立的系統基於增量備份數據離線處理的,對CynosDB的線上系統不會造成任何的影響。另外我們還需要對實例的狀態配置信息進行備份,以便恢復時能獲取到相關信息。

此文已由作者授權騰訊雲+社區發佈,更多原文請點擊

搜索關注公衆號「雲加社區」,第一時間獲取技術乾貨,關注後回覆1024 送你一份技術課程大禮包!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章