MatrixCube揭祕101——MatrixCube的功能與架構

語言: CN / TW / HK

作為新一代的資料庫系統,MatrixOne也是以當今流行的分散式架構為基礎來設計的。除了儲存引擎與計算引擎之外,分散式元件也已經成為現代資料庫設計的必選項。資料庫系統在分散式元件的支撐下展示出越來越強大的可擴充套件性和高可用性,但是同時也必須面對分散式環境中的一致性,可靠性等挑戰。

MatrixOne中的MatrixCube正是一個這樣的分散式元件,它可以將任意單機儲存引擎擴充套件成分散式的儲存引擎,儲存引擎只需要關心單機的儲存設計,而不需要去考慮分散式環境中的各種問題。MatrixCube是一個相當龐大的元件,MatrixOne社群將輸出一系列文章與教程來進行全面揭祕。

本文作為第一篇文章,將首先從功能與概念上解釋MatrixCube能做什麼,以及它的架構是什麼樣的。

MatrixCube是什麼

MatrixCube是一個Golang實現的基於Multi-Raft的自帶排程能力的分散式強一致性儲存框架。MatrixCube的設計目標是讓開發人員能夠輕鬆地實現各種強一致的分散式儲存服務。我們可以使用MatrixCube來構建一些常見的分散式儲存服務,比如:分散式Redis、分散式KV等等。而MatrixOne也是一個通過MatrixCube構建的分散式資料庫,在沒有MatrixCube的情況下,MatrixOne就是個單機的資料庫,MatrixCube使得我們可以搭建一個小型的叢集。但是MatrixCube並不與MatrixOne緊耦合,MatrixCube可以對接任意的其他儲存引擎,使得其獲得同樣的分散式儲存能力,比如我們可以讓MatrixCube對接一個RocksDB,Pebble,或者Redis。

MatrixCube作用

圖-MatrixCube的作用

MatrixCube有以下幾個功能特性:分散式,高可用,強一致,自動均衡,使用者自定義。

分散式

這個很好理解,單機系統就是一臺機器的系統,分散式系統就是很多機器組成的系統,所有分散式問題都是在解決協調多臺機器共同完成一件事情的問題。與單機只需要操作一套硬體相比,分散式環境中的多臺機器需要有大量的協調與溝通機制,同時需要對其中可能出問題的地方進行處理。比如讓計算機處理兩個數字的運算,在一臺機器上直接一套程式碼就能運行了,但是在分散式環境中我們需要設計一套機制如何將這個運算拆成不同的子任務交給不同的機器,在每臺機器完成自己的部分之後再將各自的結果通過某種機制合併到一起形成最終的結果。而這其中如果某臺機器故障,或者由於網路通訊的問題導致機器無法連線,這些異常情況都需要分散式元件進行處理,保證整個叢集仍然能完成任務。MatrixCube就是為了實現多臺機器的分散式資料儲存而實現的一套分散式框架。

分散式系統

圖-分散式系統

高可用

作為一個數據庫系統,存資料是其最起碼的職責。在一個分散式環境中,每臺機器都會有一定的出問題概率,不管是硬體環境還是軟體環境都有fail的可能性,為了能持續提供服務保證系統的可用性,我們往往會採用將同一份資料在複製多個副本的方式,將每個副本放在不同的機器上,以此來提升可用性,同時由於多副本的存在,在使用者來訪問資料的時候我們也可以通過多臺機器同時提供服務來提升系統的吞吐能力。使用MatrixCube實現的儲存服務支援高可用,由於Raft協議的選舉機制,如果一個叢集擁有2*N+1的副本數量,那麼叢集在N個副本故障的時候,還能夠正常的提供讀寫服務。

強一致

由於多副本的存在,使用者可以讀取任意節點上的副本。而強一致就是為了保證這些副本之間的資料始終都是一致的,如果在某個副本資料有更新的時候也會先將其他副本同步更新之後才會響應使用者來讀取,這樣使用者能始終讀到最新的資料。另外一個相對的概念就是最終一致性,區別在於寫入之後馬上就告訴使用者可以讀了,但是使用者如果馬上去讀其他副本的時候可能還沒有複製完成,所以仍然會讀到老資料。Matrixcube提供強一致的讀寫介面,並且承諾一旦資料寫入成功了,後續的讀操作就不會讀到一個陳舊的值,一定會讀到之前寫入的資料或者更新的資料。MatrixCube採用了Multi-Raft的方式。Raft是目前使用最廣泛的分散式一致性協議,解釋Raft協議和運作機制的文章行業內非常豐富,這裡就不詳細展開。簡單的來說Raft協議通過Leader選舉以及日誌複製的方法,保證叢集在出現如機器宕機或者故障的情況下可以始終保持對外提供資料的一致性,且始終維持最新的資料。

img

圖-強一致(Follower1)與最終一致(Follower2)

自動均衡

作為分散式儲存系統,除了通過強一致的副本複製機制保證高可用性以外,我們還應該儘可能多地將多臺機器的資源利用起來,以達到更高的使用效率。一個合格的分散式儲存應該讓每個節點之間的儲存壓力大致相同,同時對每個節點的訪問壓力大致相同,不至於在某些節點上面臨過大的儲存或者訪問壓力,這樣整個系統的效能就會因此受到影響。而MatrixCube就具備這樣的排程與自動均衡能力,可以在多節點間保持儲存與負載的均衡,並且在節點發生變化時進行儲存與訪問負載的排程,以達到重新平衡。在MatrixCube中,我們提供了三種級別的自動均衡:

  • 實現各節點儲存空間的均衡,以高效利用各節點儲存資源;
  • 各節點的 Raft-Group Leader 的均衡,由於讀寫請求都需要從Leader經過,以此來達到讀寫請求的負載均衡;
  • 各節點 Table 資料分佈的均衡,由於某些表可能是熱門資料會被頻繁訪問,以此來實現表級別的讀寫請求均衡。

img

使用者自定義

MatrixCube提供相當靈活的使用者自定義能力。MatrixCube不限制單機的資料儲存引擎,並且定義了DataStorage介面,任何實現DataStorage的儲存引擎都可以接入MatrixCube。而且MatrixCube支援自定義的讀寫請求,使用者只需要實現這些請求在單機上的邏輯,分散式相關的細節全部交給MatrixCube。因此使用者可以非常方便地接入各種不同的單機儲存引擎,以及自定義各種不同的讀寫請求命令。

MatrixCube的架構與運作機制

MatrixCube基本概念

我們需要先了解一些概念來幫助我們更好地理解MatrixCube。

  • Store:MatrixCube是一個分散式儲存的框架,所以我們的資料會存放在很多的節點上,我們把叢集中的一個節點稱為一個Store。
  • Shard:資料在MatrixCube叢集中是分片儲存的,每個資料分片我們稱之為一個Shard。一個Store中可以管理多個Shard。
  • Replica:為了保證儲存服務的高可用,每個Shard的資料是儲存多份的,並且分佈在不同的Store上,Shard的一個數據副本我們稱之為一個Replica。所以一個Shard會包含多個Replica,每個Replica中的資料都是一樣的。
  • Raft-Group:通過多副本我們保證了資料的高可用,為了保證資料的一致性,我們採用Raft協議來做資料共識,一個Shard的多個Replica會組成一個Raft-Group。

MatrixCube功能元件

  • DataStorage: 使用MatrixCube就必須要定義一個DataStorage,用來儲存單機資料。我們需要針對儲存服務的特點來設計對應的DataStorage。MatrixCube預設提供了一個完整的基於KV的DataStorage,因為基於KV的儲存可以滿足大部分的場景。

  • Prophet: Prophet是一個排程模組,主要職責是負責Auto-Rebalance以及維持每個Shard的Replica個數。每個Store以及Shard的Leader Replica都會定期上報心跳資訊給Prophet,Prophet會根據這些資訊來做出排程決定。MatrixCube需要在叢集中指定哪些節點承擔排程的職責。

  • Raftstore: Raftstore是MatrixCube最核心的元件, 其中實現了Store,Shard,Raft-Log相關的元資料儲存,對Multi-Raft協議的支援,全域性路由表的構建以及每個節點上的讀寫Shard Proxy代理功能。

MatrixCube的整體架構

img

圖- MatrixCube整體架構

MatrixCube的工作機制

系統啟動與配置

在系統初始化的時候,MatrixCube會根據叢集中每個節點的配置檔案來進行初始化。在MatrixCube中,一共有兩種型別的節點,一種是排程節點,也就是Prophet節點,另一種是資料節點,只存資料,沒有排程功能。兩種節點之間目前無法互相轉換,都是在系統初始化的時候指定好的。MatrixCube的最初三個節點必須都是Prophet節點,這也是MatrixCube所能組成的最小規模叢集。Prophet的三個節點構成一個Raft-Group,其中會選舉出一個leader,所有的排程請求與資訊上報都會到這個Prophet Leader上。Prophet的節點數可以進行配置,但是以Raft協議為基準,必須是2*N+1個節點(N為不小於0的整數)。

資料儲存與分裂

在系統開始啟動執行之後,使用者開始往系統中匯入資料。MatrixCube在初始化時已讀入一個數據分片Shard的大小配置,比如1GB一個Shard。使用者寫入資料還沒有達到1GB的時候,會持續寫入同一個Shard,而同時每次寫入的資料會同步在三個節點中更新Shard,這三個Shard會構成一個Raft-Group,其中也會選舉出一個Leader,對這個Shard的讀寫請求與資訊上報全部會由這個Leader來處理。直到一個Shard達到1GB時,此時MatrixCube會啟動Shard分裂機制,也就是把一個Shard均勻拆分成兩個Shard,也就是1個GB的分片變成了2個500MB的分片,而這個過程是同步在三個節點中發生的,也就是說同時將生成6個Shard,而他們將組成新的2個Raft-Group。

Shard分裂

圖-Shard分裂

使用者讀寫響應與路由

使用者以介面的形式與MatrixCube互動,從而來發起對資料的讀寫請求。由於Raft-Group的存在,只有每個Raft-Group的Leader才能響應讀寫請求。而使用者的請求可能並不一定直接指向Raft Group Leader,因此我們就需要一個路由機制,來讓使用者的請求能被正確的轉到它該去的地方。因此每個節點上都會有一個Shard Proxy,這些Proxy會定期接收Prophet給它的全域性路由表。我們前面提到Prophet會接受各個Store與各個Shard的Leader給的心跳資訊,因此Prophet是有一張全域性的路由資訊表的。這個時候Shard Proxy就會知道使用者請求讀寫那份資料應該在哪個Store的哪個Shard中。這樣的話使用者無論向叢集中哪個節點發起讀寫請求,最終得到的結果都是一樣的,使用者不需要關心Shard在內部存在什麼地方。

假設我們有一個3個Store節點的叢集,初始狀態如下:

  Range Store1 Store2 Store3
Shard1 [key1-key10) Leader Follower Follower
Shard2 [key10-key20) Follower Leader Follower
Shard3 [key20-key30) Follower Follower Leader

使用者分別向key1,key10和key20資料傳送請求,下圖說明了請求如何通過Shard Proxy路由元件並被轉發。

Shard Proxy對使用者請求的轉發

圖-Shard Proxy對使用者請求的轉發

節點變化與資料搬遷

在整個叢集發生節點變化時,比如叢集需要擴縮容,或者出現機器或者網路故障的時候,我們就要進行叢集的排程。通常來講,我們的Prophet會有一個超時機制,在某個節點超過我們規定的時間沒有心跳上報的時候,我們這時就認為這個節點已經下線,我們將開始資料搬遷流程。在節點減少的情況下,Prophet會檢測到一部分Shard沒有足夠的Replica組成一個完整的Raft-Group,這時就需要在現有的節點中找到儲存空間比較富餘的節點,在其中建立這一部分Shard的Replica。而在節點增加的情況下,Prophet會發現多出的節點儲存空間比較富餘,這時會將叢集中的一部分Shard進行重新分配,以達到一個各節點相對均衡的狀態。在節點變化的時候,除了Prophet會進行排程意外,每個被破壞的Shard組成的Raft-Group也會視情況進行排程,如果少了Raft-Group的follower,那在完成Shard的新Replica生成後會重新組成完整的Raft-Group;如果少了Raft-Group的Leader,那剩下的兩份Replica就會先選舉出新的Leader,再去新的節點上完成Replica與Raft-Group的生成。

在下圖的例子中我們看到,初始狀態的三節點在增加第四節點的情況下,每個節點的4個Shard被均攤到了4個節點上,而本來不均衡的Leader數量也均攤到了每個節點上

節點變化與資料搬遷

圖-節點增加時的資料搬遷

在這些機制的組合工作下,MatrixCube就可以將一個單機的儲存引擎擴充套件到一個分散式場景中來使用。

MatrixCube與TiKV的區別

很多社群的小夥伴在之前都接觸過TiDB分散式資料庫的架構,以及其中負責提供分散式能力的元件TiKV以及負責排程的模組Placement Driver。實際上MatrixCube的功能基本相當於TiKV與PD的結合,MatrixCube除了還在開發中的分散式事務能力以外,其他的高可用,強一致以及排程的能力與TiKV+PD基本保持一致。

MatrixCube與TiKV+PD主要有三點區別:

  • TiKV+PD是一個完整的服務,所有的讀寫細節都已經在內部封裝完成,使用者通過與其定義好的介面與其進行互動。而MatrixCube是一個Library,無法單獨執行,必須與儲存引擎一起工作。而且MatrixCube將讀寫的請求命令交給了使用者定義,使用者可以自行去實現各自不同儲存引擎的讀寫請求命令,只需要實現MatrixCube提供的儲存引擎介面即可與MatrixCube對接,由MatrixCube負責各自分散式的相關細節。
  • PD的排程功能主要體現在儲存空間的排程與Raft Group的Leader的排程,可以在副本級別達到負載均衡。而MatrixCube除了在實現這兩點外,還實現了表級別的資料分佈均衡,從而使得對錶的讀寫請求也能達到相對均衡的狀態。
  • TiKV由Rust實現,而PD是由Go實現。因此這套結構在對接過程中需要一定的中間層介面。而MatrixCube整體都是由Go所實現,因此不存在這樣的問題。

總的來說MatrixCube更加看重開發靈活性,開發者可以非常靈活的應用MatrixCube去實現不同地分散式儲存引擎。

下期預告

為了向開發者展示MatrixCube的使用,MatrixOne社群準備了一個非常簡單的KV儲存的例子,用MatrixCube對接Pebble儲存引擎實現了一個分散式儲存系統。

 

MatrixOne 社群

歡迎新增 MO 小助手微信 → ID:MatrixOrigin001,加入 MatrixOne 社群參與討論!

官網:matrixorigin.cn

原始碼:github.com/matrixorigin/matrixone

Slack:matrixoneworkspace.slack.com

知乎 | CSDN | 墨天輪 | InfoQ | SegmentFault:MatrixOrigin