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