其實不然,MySQL 并沒有過時,本篇文章的主角 —— RadonDB ,就是把 NewSQL 領(lǐng)域比較流行的分布式一致性算法和 MySQL 結(jié)合起來,形成了新一代的分布式數(shù)據(jù)庫 MyNewSQL,同樣做到了可擴展、高可用、強一致、易部署的特點。
那么,如何將 NewSQL 領(lǐng)域比較流行的技術(shù)和 MySQL 結(jié)合起來,打造一款新的分布式數(shù)據(jù)庫?

1、RadonDB 的架構(gòu)
首先看一下 RadonDB 的架構(gòu),如上圖所示,上半部分是分布式的 SQL 層,下面是存儲層,如果我們對 F1 和 Spanner 進行抽象之后,會發(fā)現(xiàn)也都是這兩層。
其中 SQL 層主要負責對用戶 SQL 解析,然后生成分布式的執(zhí)行計劃和執(zhí)行器,再把這些執(zhí)行器下發(fā)到具體的存儲節(jié)點去執(zhí)行。
雖然架構(gòu)看上去比較一致,但 RadonDB 比較特殊的一點是:下面的存儲層有多個存儲節(jié)點。圖中每個圓圈里面都是一個存儲節(jié)點,每個存儲節(jié)點有三副本,三副本之間就是一個 Raft 協(xié)議進行數(shù)據(jù)同步,每個副本都是一個 MySQL。而其他 NewSQL 就是一個 KV 或者其他的存儲。
2、RadonDB 架構(gòu)層解析
下面詳細闡述一下架構(gòu)里面的各個技術(shù)點。
SQL 節(jié)點

首先來看一下 SQL 節(jié)點。
用戶請求到達 SQL 節(jié)點后,我們根據(jù)數(shù)據(jù)的分布規(guī)則生成一個分布式的執(zhí)行計劃,告訴用戶的 SQL 要分發(fā)到哪些存儲節(jié)點,然后根據(jù)分布式執(zhí)行計劃生成一個分布式的執(zhí)行器,就是具體到哪些存儲節(jié)點進行鏈接、執(zhí)行、返回。
執(zhí)行完之后 SQL 節(jié)點就會做二次運算,為什么叫二次運算?因為下面是 MySQL,SQL 節(jié)點把計算推到 MySQL 之后,SQL 節(jié)點再進行二次運算,包括 limit/groupby/aggregation/join。
所以說,SQL 節(jié)點是一個無中心化、無狀態(tài)的,擴容性強。
3、存儲層

存儲層由多個 Node 組成,每個 Node 就是一主兩從的 MySQL,但這個 MySQL 比較特殊,因為 MySQL 沒有一個高可用的方案,可能大家都是 MHA 或者自己寫一個主從切換腳本來運維。
但是 RadonDB 引入了 Raft 協(xié)議,它是無中心化的, 當主庫掛了后,通過 Raft 協(xié)議選擇新主,而數(shù)據(jù)同步則基于 MySQL GTID 機制。
基于 MySQL 的好處是不僅有存儲能力還有計算能力,如果一個副本只是一個 KV ,他的計算能力就比較有限,SQL 層把數(shù)據(jù)推到存儲層,然后再返回 SQL 節(jié)點再進行運算,這樣存儲層和 SQL 層交互就會比較多。
我們盡量把計算能力下推到存儲層讓 MySQL 完成,因為 MySQL 跟數(shù)據(jù)是在一塊的,不涉及網(wǎng)絡(luò)傳輸,只需要幾個 I/O 就將數(shù)據(jù)過濾掉了。

4、數(shù)據(jù)分布
剛才說了 SQL 層和存儲層,再看一下數(shù)據(jù)怎么分布?
建一個 T1 表,后面指定的分區(qū)方式是 HASH,在 RadonDB 里面默認整張表共 4096 slots, 每個小表默認是 128 slots, 其實就是一個大表分成 32 個小表,比如兩個存儲節(jié)點,這個 T1 表的 32 個小表,前 16 個小表在第一個存儲節(jié)點上,后 16 個小表是在第二個節(jié)點上,是均分布的。
可能很多人認為基于 MySQL 擴容是個問題,但是如上所說,表分完之后,RadonDB 以小表為單位做數(shù)據(jù)遷移,所以擴容非常方便。如果是加了一個新的節(jié)點,RadonDB 就會把動態(tài)的一些小表遷移到新的節(jié)點上,因為我們是基于 MySQL 做的,首先會做一個全量,然后把位點記下來,等全量做完再追增量,這個遷移過程基本不影響業(yè)務(wù)了。
所以這樣每個小表就可以在多個存儲節(jié)點上動態(tài)的漂移。這些遷移規(guī)則也可以進行自定制,比如說先遷移較大的表或者熱度比較高的表,讓整體資源分配最快達到最優(yōu)化。
5、如何保障高可用?

一個存儲節(jié)點內(nèi)三個副本怎么保證高可用的?我們將分布式一致性算法 Raft 和 MySQL 自身的 GTID 結(jié)合起來。
Raft 主要做兩件事,一個是選主,第二個是數(shù)據(jù)同步。MySQL 5.7 GTID,類似于 Raft 里面的一個 log index, 數(shù)據(jù)同步是通過 GTID,選主是通過 Raft,我們開發(fā)了一套 Raft 框架,實時監(jiān)測 MySQL 狀態(tài),如果主不正常了,就發(fā)起重新選主。
選完后新主與其他兩個從庫數(shù)據(jù)怎么同步呢?兩個從根據(jù)自己的 GTID 向主那去拉數(shù)據(jù),進行數(shù)據(jù)同步。MySQL 5.7 可以并行復制,過程非常迅速,主從基本沒有延遲,在高壓情況下延遲也非常小。而且,通過比較強的 semi-sync 確保事務(wù)不丟失。
存儲節(jié)點里 Raft 和 GTID 是沒有中心化的,可以跨機房部署,非常靈活。
6、分布式事務(wù)
下面看一下分布式事務(wù),為什么分布式數(shù)據(jù)庫需要分布式事務(wù)呢?
因為數(shù)據(jù)在存儲節(jié)點是分布式存儲的,比如說一個表在節(jié)點 1、節(jié)點 2、節(jié)點 3 都有存儲,然后執(zhí)行了一個操作,在節(jié)點 1 成功了,在節(jié)點 2 失敗了,節(jié)點 3 成功了。這時如果沒有分布式事務(wù),那這個表其實是壞的。
如果沒有分布式事務(wù)保證的話,數(shù)據(jù)隨時都處于不可用的狀態(tài),只能用來存不重要的業(yè)務(wù)。
所以 RadonDB 就提供了分布式事務(wù),比如說剛才這個情況,就是 2 失敗之后,這個 1 和 3 存儲節(jié)點自動回滾,這是分布式事務(wù)保障。
RadonDB 分布式事務(wù)也是基于 MySQL 實現(xiàn)的,在 MySQL 里面分兩階段提交。
首先它會做 xa start,然后執(zhí)行 SQL,做 xa end,第四做 xa prepare,這是第一階段,此時事務(wù)才會從副本復制過去;第二個階段是 xa commit。
所以 RadonDB 在 SQL 層進行了事務(wù)管理,把 MySQL 的五個步驟抽象成三個,第一是 Begin,第二是執(zhí)行,第三是提交,如果 Prepare 失敗,可以進行 Rollback。
提到分布式事務(wù),大家可能會問,這個事務(wù)是什么隔離級別?
RadonDB 實現(xiàn) Snapshot Isolation 也就是快照隔離級別,這是個什么概念呢?當部分分區(qū)沒有提交時,這個事務(wù)對其他事務(wù)不可見,這就是部分提交不可見。另外就是未提交不可見,也就是說做了 prepare,但沒有 Commit,那也是不可見的。
7、SI 隔離級別

大家可以看一下上圖右邊兩個 SQL 語句,兩個 Client 連上來,一個 Client 是掃表,第二個 SQL 語句就是不停更新這個表。
對于 SI 隔離級別我們用了 XeLabs/go-jepsen ,1 個更新線程,16 掃表線程,通過 100 多億次操作和監(jiān)測沒有發(fā)現(xiàn)問題,并且可以隨機 KILL 存儲節(jié)點主副本,這些都證明 MySQL XA 已經(jīng)很強大了。
RadonDB 還支持 HTAP 混合模式,在傳統(tǒng)的解決方案里,一般都是兩套系統(tǒng),就是兩個端口。在需要事務(wù)和需要分析的時候,分別在兩個端口處理,中間通過ETL通道進行數(shù)據(jù)同步。
但是,在 RadonDB 里就一個端口,如果是 OLAP 的操作,我們會自動路由到計算節(jié)點,而且 OLTP 和 OLAP 這兩個計算的資源是隔離的,互不影響。
8、性能

最后看一看 RadonDB 的性能。上圖是單機 MySQL 和四個存儲節(jié)點的 RadonDB 的對比。
我們用 sysbench 16 個表、512 個線程,隨機寫了 5000 萬條數(shù)據(jù),測試得出來的結(jié)果,RadonDB 基本上可以做到 26589 TBS,單機是 9346 TBS,可以看到在 TBS 層面 RadonDB 性能將近是單機的三倍,延遲卻只有的三分之一。
這就是分布式數(shù)據(jù)庫的威力,性能和容量可以通過節(jié)點的增加而線性增長。
作者簡介

張雁飛,青云QingCloud 數(shù)據(jù)庫高級技術(shù)專家。TokuDB 內(nèi)核貢獻者、維護者,TokuDB 企業(yè)級熱備工具作者。
曾就職于阿里云數(shù)據(jù)庫