其實(shí)不然,MySQL 并沒(méi)有過(guò)時(shí),本篇文章的主角 —— RadonDB ,就是把 NewSQL 領(lǐng)域比較流行的分布式一致性算法和 MySQL 結(jié)合起來(lái),形成了新一代的分布式數(shù)據(jù)庫(kù) MyNewSQL,同樣做到了可擴(kuò)展、高可用、強(qiáng)一致、易部署的特點(diǎn)。
那么,如何將 NewSQL 領(lǐng)域比較流行的技術(shù)和 MySQL 結(jié)合起來(lái),打造一款新的分布式數(shù)據(jù)庫(kù)?
1、RadonDB 的架構(gòu)
首先看一下 RadonDB 的架構(gòu),如上圖所示,上半部分是分布式的 SQL 層,下面是存儲(chǔ)層,如果我們對(duì) F1 和 Spanner 進(jìn)行抽象之后,會(huì)發(fā)現(xiàn)也都是這兩層。
其中 SQL 層主要負(fù)責(zé)對(duì)用戶 SQL 解析,然后生成分布式的執(zhí)行計(jì)劃和執(zhí)行器,再把這些執(zhí)行器下發(fā)到具體的存儲(chǔ)節(jié)點(diǎn)去執(zhí)行。
雖然架構(gòu)看上去比較一致,但 RadonDB 比較特殊的一點(diǎn)是:下面的存儲(chǔ)層有多個(gè)存儲(chǔ)節(jié)點(diǎn)。圖中每個(gè)圓圈里面都是一個(gè)存儲(chǔ)節(jié)點(diǎn),每個(gè)存儲(chǔ)節(jié)點(diǎn)有三副本,三副本之間就是一個(gè) Raft 協(xié)議進(jìn)行數(shù)據(jù)同步,每個(gè)副本都是一個(gè) MySQL。而其他 NewSQL 就是一個(gè) KV 或者其他的存儲(chǔ)。
2、RadonDB 架構(gòu)層解析
下面詳細(xì)闡述一下架構(gòu)里面的各個(gè)技術(shù)點(diǎn)。
SQL 節(jié)點(diǎn)
首先來(lái)看一下 SQL 節(jié)點(diǎn)。
用戶請(qǐng)求到達(dá) SQL 節(jié)點(diǎn)后,我們根據(jù)數(shù)據(jù)的分布規(guī)則生成一個(gè)分布式的執(zhí)行計(jì)劃,告訴用戶的 SQL 要分發(fā)到哪些存儲(chǔ)節(jié)點(diǎn),然后根據(jù)分布式執(zhí)行計(jì)劃生成一個(gè)分布式的執(zhí)行器,就是具體到哪些存儲(chǔ)節(jié)點(diǎn)進(jìn)行鏈接、執(zhí)行、返回。
執(zhí)行完之后 SQL 節(jié)點(diǎn)就會(huì)做二次運(yùn)算,為什么叫二次運(yùn)算?因?yàn)橄旅媸?MySQL,SQL 節(jié)點(diǎn)把計(jì)算推到 MySQL 之后,SQL 節(jié)點(diǎn)再進(jìn)行二次運(yùn)算,包括 limit/groupby/aggregation/join。
所以說(shuō),SQL 節(jié)點(diǎn)是一個(gè)無(wú)中心化、無(wú)狀態(tài)的,擴(kuò)容性強(qiáng)。
3、存儲(chǔ)層
存儲(chǔ)層由多個(gè) Node 組成,每個(gè) Node 就是一主兩從的 MySQL,但這個(gè) MySQL 比較特殊,因?yàn)?MySQL 沒(méi)有一個(gè)高可用的方案,可能大家都是 MHA 或者自己寫一個(gè)主從切換腳本來(lái)運(yùn)維。
但是 RadonDB 引入了 Raft 協(xié)議,它是無(wú)中心化的, 當(dāng)主庫(kù)掛了后,通過(guò) Raft 協(xié)議選擇新主,而數(shù)據(jù)同步則基于 MySQL GTID 機(jī)制。
基于 MySQL 的好處是不僅有存儲(chǔ)能力還有計(jì)算能力,如果一個(gè)副本只是一個(gè) KV ,他的計(jì)算能力就比較有限,SQL 層把數(shù)據(jù)推到存儲(chǔ)層,然后再返回 SQL 節(jié)點(diǎn)再進(jìn)行運(yùn)算,這樣存儲(chǔ)層和 SQL 層交互就會(huì)比較多。
我們盡量把計(jì)算能力下推到存儲(chǔ)層讓 MySQL 完成,因?yàn)?MySQL 跟數(shù)據(jù)是在一塊的,不涉及網(wǎng)絡(luò)傳輸,只需要幾個(gè) I/O 就將數(shù)據(jù)過(guò)濾掉了。
4、數(shù)據(jù)分布
剛才說(shuō)了 SQL 層和存儲(chǔ)層,再看一下數(shù)據(jù)怎么分布?
建一個(gè) T1 表,后面指定的分區(qū)方式是 HASH,在 RadonDB 里面默認(rèn)整張表共 4096 slots, 每個(gè)小表默認(rèn)是 128 slots, 其實(shí)就是一個(gè)大表分成 32 個(gè)小表,比如兩個(gè)存儲(chǔ)節(jié)點(diǎn),這個(gè) T1 表的 32 個(gè)小表,前 16 個(gè)小表在第一個(gè)存儲(chǔ)節(jié)點(diǎn)上,后 16 個(gè)小表是在第二個(gè)節(jié)點(diǎn)上,是均分布的。
可能很多人認(rèn)為基于 MySQL 擴(kuò)容是個(gè)問(wèn)題,但是如上所說(shuō),表分完之后,RadonDB 以小表為單位做數(shù)據(jù)遷移,所以擴(kuò)容非常方便。如果是加了一個(gè)新的節(jié)點(diǎn),RadonDB 就會(huì)把動(dòng)態(tài)的一些小表遷移到新的節(jié)點(diǎn)上,因?yàn)槲覀兪腔?MySQL 做的,首先會(huì)做一個(gè)全量,然后把位點(diǎn)記下來(lái),等全量做完再追增量,這個(gè)遷移過(guò)程基本不影響業(yè)務(wù)了。
所以這樣每個(gè)小表就可以在多個(gè)存儲(chǔ)節(jié)點(diǎn)上動(dòng)態(tài)的漂移。這些遷移規(guī)則也可以進(jìn)行自定制,比如說(shuō)先遷移較大的表或者熱度比較高的表,讓整體資源分配最快達(dá)到最優(yōu)化。
5、如何保障高可用?
一個(gè)存儲(chǔ)節(jié)點(diǎn)內(nèi)三個(gè)副本怎么保證高可用的?我們將分布式一致性算法 Raft 和 MySQL 自身的 GTID 結(jié)合起來(lái)。
Raft 主要做兩件事,一個(gè)是選主,第二個(gè)是數(shù)據(jù)同步。MySQL 5.7 GTID,類似于 Raft 里面的一個(gè) log index, 數(shù)據(jù)同步是通過(guò) GTID,選主是通過(guò) Raft,我們開發(fā)了一套 Raft 框架,實(shí)時(shí)監(jiān)測(cè) MySQL 狀態(tài),如果主不正常了,就發(fā)起重新選主。
選完后新主與其他兩個(gè)從庫(kù)數(shù)據(jù)怎么同步呢??jī)蓚(gè)從根據(jù)自己的 GTID 向主那去拉數(shù)據(jù),進(jìn)行數(shù)據(jù)同步。MySQL 5.7 可以并行復(fù)制,過(guò)程非常迅速,主從基本沒(méi)有延遲,在高壓情況下延遲也非常小。而且,通過(guò)比較強(qiáng)的 semi-sync 確保事務(wù)不丟失。
存儲(chǔ)節(jié)點(diǎn)里 Raft 和 GTID 是沒(méi)有中心化的,可以跨機(jī)房部署,非常靈活。
6、分布式事務(wù)
下面看一下分布式事務(wù),為什么分布式數(shù)據(jù)庫(kù)需要分布式事務(wù)呢?
因?yàn)閿?shù)據(jù)在存儲(chǔ)節(jié)點(diǎn)是分布式存儲(chǔ)的,比如說(shuō)一個(gè)表在節(jié)點(diǎn) 1、節(jié)點(diǎn) 2、節(jié)點(diǎn) 3 都有存儲(chǔ),然后執(zhí)行了一個(gè)操作,在節(jié)點(diǎn) 1 成功了,在節(jié)點(diǎn) 2 失敗了,節(jié)點(diǎn) 3 成功了。這時(shí)如果沒(méi)有分布式事務(wù),那這個(gè)表其實(shí)是壞的。
如果沒(méi)有分布式事務(wù)保證的話,數(shù)據(jù)隨時(shí)都處于不可用的狀態(tài),只能用來(lái)存不重要的業(yè)務(wù)。
所以 RadonDB 就提供了分布式事務(wù),比如說(shuō)剛才這個(gè)情況,就是 2 失敗之后,這個(gè) 1 和 3 存儲(chǔ)節(jié)點(diǎn)自動(dòng)回滾,這是分布式事務(wù)保障。
RadonDB 分布式事務(wù)也是基于 MySQL 實(shí)現(xiàn)的,在 MySQL 里面分兩階段提交。
首先它會(huì)做 xa start,然后執(zhí)行 SQL,做 xa end,第四做 xa prepare,這是第一階段,此時(shí)事務(wù)才會(huì)從副本復(fù)制過(guò)去;第二個(gè)階段是 xa commit。
所以 RadonDB 在 SQL 層進(jìn)行了事務(wù)管理,把 MySQL 的五個(gè)步驟抽象成三個(gè),第一是 Begin,第二是執(zhí)行,第三是提交,如果 Prepare 失敗,可以進(jìn)行 Rollback。
提到分布式事務(wù),大家可能會(huì)問(wèn),這個(gè)事務(wù)是什么隔離級(jí)別?
RadonDB 實(shí)現(xiàn) Snapshot Isolation 也就是快照隔離級(jí)別,這是個(gè)什么概念呢?當(dāng)部分分區(qū)沒(méi)有提交時(shí),這個(gè)事務(wù)對(duì)其他事務(wù)不可見,這就是部分提交不可見。另外就是未提交不可見,也就是說(shuō)做了 prepare,但沒(méi)有 Commit,那也是不可見的。
7、SI 隔離級(jí)別
大家可以看一下上圖右邊兩個(gè) SQL 語(yǔ)句,兩個(gè) Client 連上來(lái),一個(gè) Client 是掃表,第二個(gè) SQL 語(yǔ)句就是不停更新這個(gè)表。
對(duì)于 SI 隔離級(jí)別我們用了 XeLabs/go-jepsen ,1 個(gè)更新線程,16 掃表線程,通過(guò) 100 多億次操作和監(jiān)測(cè)沒(méi)有發(fā)現(xiàn)問(wèn)題,并且可以隨機(jī) KILL 存儲(chǔ)節(jié)點(diǎn)主副本,這些都證明 MySQL XA 已經(jīng)很強(qiáng)大了。
RadonDB 還支持 HTAP 混合模式,在傳統(tǒng)的解決方案里,一般都是兩套系統(tǒng),就是兩個(gè)端口。在需要事務(wù)和需要分析的時(shí)候,分別在兩個(gè)端口處理,中間通過(guò)ETL通道進(jìn)行數(shù)據(jù)同步。
但是,在 RadonDB 里就一個(gè)端口,如果是 OLAP 的操作,我們會(huì)自動(dòng)路由到計(jì)算節(jié)點(diǎn),而且 OLTP 和 OLAP 這兩個(gè)計(jì)算的資源是隔離的,互不影響。
8、性能
最后看一看 RadonDB 的性能。上圖是單機(jī) MySQL 和四個(gè)存儲(chǔ)節(jié)點(diǎn)的 RadonDB 的對(duì)比。
我們用 sysbench 16 個(gè)表、512 個(gè)線程,隨機(jī)寫了 5000 萬(wàn)條數(shù)據(jù),測(cè)試得出來(lái)的結(jié)果,RadonDB 基本上可以做到 26589 TBS,單機(jī)是 9346 TBS,可以看到在 TBS 層面 RadonDB 性能將近是單機(jī)的三倍,延遲卻只有的三分之一。
這就是分布式數(shù)據(jù)庫(kù)的威力,性能和容量可以通過(guò)節(jié)點(diǎn)的增加而線性增長(zhǎng)。
作者簡(jiǎn)介
張雁飛,青云QingCloud 數(shù)據(jù)庫(kù)高級(jí)技術(shù)專家。TokuDB 內(nèi)核貢獻(xiàn)者、維護(hù)者,TokuDB 企業(yè)級(jí)熱備工具作者。
曾就職于阿里云數(shù)據(jù)庫(kù)