螞蟻關(guān)于 TuGraph-DB 圖數(shù)據(jù)庫(kù)高可用架構(gòu)介紹
一、高可用架構(gòu)介紹
1. 高可用架構(gòu)是什么
首先來(lái)看一個(gè)問(wèn)題,正常訪(fǎng)問(wèn)網(wǎng)絡(luò)上一個(gè)服務(wù)的流程是,提交一個(gè) request,然后服務(wù)進(jìn)行一定的處理,返回給我們一個(gè) success 的 response。但有時(shí)會(huì)因?yàn)榫W(wǎng)絡(luò)阻塞、資源不足,甚至黑客網(wǎng)絡(luò)攻擊或硬件毀損等原因,導(dǎo)致服務(wù)不能返回一個(gè)正確的 response,那么這時(shí)作為一個(gè)線(xiàn)上的業(yè)務(wù),就是不可用的,可能會(huì)造成非常巨大的損失。
2. 高可用性代表系統(tǒng)的可用性程度,是進(jìn)行系統(tǒng)設(shè)計(jì)時(shí)的準(zhǔn)則之一
怎樣去衡量系統(tǒng)的可用性和不可用性呢?這就引出了高可用性的概念。高可用性代表系統(tǒng)的可用性程度,是進(jìn)行系統(tǒng)設(shè)計(jì)的準(zhǔn)則之一。高可用性,是系統(tǒng)的一個(gè)非常重要的能力,通常是通過(guò)提高系統(tǒng)的容錯(cuò)能力來(lái)實(shí)現(xiàn)的??捎眯缘囊粋€(gè)度量方式是根據(jù)系統(tǒng)損毀無(wú)法提供服務(wù)的時(shí)間和系統(tǒng)正常運(yùn)行時(shí)間的比值來(lái)得到的。下圖右側(cè)表格展示了衡量一個(gè)系統(tǒng)可用性和不可用性的等級(jí)。
TuGraph-DB 對(duì)于可用性的要求,至少是 4 個(gè) 9 級(jí)別,也就是一年之內(nèi)宕機(jī)時(shí)間不能超過(guò) 53 分鐘。我們?cè)陂_(kāi)源之前服務(wù)的一個(gè)銀行用戶(hù)就已經(jīng)達(dá)到了一個(gè)極高可用的等級(jí),也就是 5 個(gè) 9 的等級(jí)。
3. 常用高可用架構(gòu)--主備復(fù)制
以上是高可用性的定義,為了達(dá)到高可用性,就需要去設(shè)計(jì)高可用架構(gòu)。比較簡(jiǎn)單的高可用架構(gòu),就是主備復(fù)制模式。主備復(fù)制模式有一個(gè)對(duì)外提供服務(wù)的主服務(wù)器,其它服務(wù)器作為其備機(jī)。主備復(fù)制模式最重要的特點(diǎn)是定期復(fù)制主機(jī)的數(shù)據(jù)給備機(jī),比如一個(gè)小時(shí)、一天。當(dāng)主機(jī)宕機(jī)之后,可以通過(guò)人工手段,也可以通過(guò) NGINX 做請(qǐng)求轉(zhuǎn)發(fā)等自動(dòng)的方式,讓備節(jié)點(diǎn)成為主節(jié)點(diǎn)。
這種模式非常簡(jiǎn)單,只復(fù)制數(shù)據(jù),也因?yàn)榭蛻?hù)只通過(guò) URL 訪(fǎng)問(wèn)對(duì)應(yīng)的服務(wù),因此是沒(méi)有感知的。但其弊端是需要人工干預(yù),而更重要的問(wèn)題是可能會(huì)丟失部分?jǐn)?shù)據(jù)。不管以多快的頻率進(jìn)行備份,一個(gè)小時(shí),甚至十幾分鐘,都可能會(huì)丟失一部分?jǐn)?shù)據(jù),也就無(wú)法達(dá)成高可用的要求。這種模式比較適合于管理系統(tǒng),比如學(xué)生管理系統(tǒng),沒(méi)有高頻率的寫(xiě)更新。MySQL、Redis、Mongodb 的高可用架構(gòu),基本上官方開(kāi)源提供的都是這種模式。
4. TuGraph-DB 高可用架構(gòu)—Raft 共識(shí)算法
針對(duì)上述缺點(diǎn),我們選用了 Raft 算法來(lái)實(shí)現(xiàn)高可用架構(gòu)。Raft 算法的優(yōu)點(diǎn)包括:
- 首先,它保持了一主多備的易用性。它有一個(gè)強(qiáng) leader 可以對(duì)外提供服務(wù)。
- 第二是一致性。一主多備的模式是通過(guò)定期復(fù)制的方式去進(jìn)行數(shù)據(jù)備份。但是 Raft 算法采用的是日志復(fù)制方式,復(fù)制的是日志并不是數(shù)據(jù),當(dāng)寫(xiě)請(qǐng)求的日志到來(lái)之后,會(huì)逐個(gè)按順序發(fā)送給每一個(gè)節(jié)點(diǎn),當(dāng)超過(guò)半數(shù)的節(jié)點(diǎn)達(dá)成一致之后才會(huì)提交,所以它不僅不會(huì)丟失數(shù)據(jù),甚至也不會(huì)存在日志空洞或亂序的情況。
- 有了一致性的保證后,安全性也就有了保證,當(dāng)超過(guò)半數(shù)的節(jié)點(diǎn)達(dá)成一致之后,才應(yīng)用日志,這樣就能解決網(wǎng)絡(luò)分區(qū)延遲、丟包、冗余和亂序的錯(cuò)誤。
- 基于一致性和安全性,它的可用性也就得到了保證,只要少于半數(shù)的節(jié)點(diǎn)宕機(jī),即使主機(jī)宕機(jī),也可以快速恢復(fù)應(yīng)用,通過(guò)一次選舉的時(shí)間就可以重新選出一個(gè) leader 對(duì)外提供服務(wù)。
PPT 右下角的表格是國(guó)標(biāo)對(duì)于高可用系統(tǒng)的指標(biāo)評(píng)估,RTO 和 RPO 分別是恢復(fù)時(shí)間指標(biāo)和恢復(fù)點(diǎn)目標(biāo),有 6 個(gè)等級(jí),TuGraph-DB 已經(jīng)達(dá)到了最高等級(jí)。當(dāng)少量節(jié)點(diǎn)故障時(shí),RPO 是 0,也就是沒(méi)有數(shù)據(jù)損失,數(shù)據(jù)恢復(fù)時(shí)間點(diǎn)指標(biāo)是小于 15 秒。即使是在部署的時(shí)候,無(wú)論是在同城的兩中心、三中心,還是多地的多中心,都可以達(dá)成 RTO 小于 15 秒的標(biāo)準(zhǔn)。
二、TuGraph-DB 高可用架構(gòu)與規(guī)劃
1. Server 架構(gòu)設(shè)計(jì)—啟動(dòng)集群
上面是我們選擇的一個(gè)基礎(chǔ)算法,下面介紹 TuGraph-DB 具體的高可用架構(gòu)是怎樣設(shè)計(jì)的。通過(guò)一個(gè) CASE 來(lái)進(jìn)行講解。
首先建立一個(gè)集群,啟動(dòng)集群的方式跟單機(jī)版幾乎是一致的,只不過(guò)要加上 enable_ha 參數(shù)和 ha_conf 參數(shù)去指定集群里面所有節(jié)點(diǎn)的 URL,并且要保證三個(gè)或者五個(gè)節(jié)點(diǎn)是可以進(jìn)行通信的。在三個(gè)節(jié)點(diǎn)同時(shí)啟動(dòng)后,最先啟動(dòng)的節(jié)點(diǎn)的計(jì)時(shí)器會(huì)超時(shí),把自己選成一個(gè)候選者,之后向其它節(jié)點(diǎn)發(fā)送一個(gè)投票請(qǐng)求,其它節(jié)點(diǎn)接收到請(qǐng)求之后,會(huì)返回給候選者一個(gè) success 的 response,當(dāng)超過(guò)半數(shù)之后,這個(gè) leader 就當(dāng)選了。一般來(lái)說(shuō),在同城的情況下,延遲不會(huì)超過(guò) 2 毫秒,在兩地的情況下,比如上海到深圳,最高延遲不會(huì)超過(guò) 30 毫秒,所以集群建立的時(shí)間和選舉的時(shí)間是非??斓?。
2. Server 架構(gòu)設(shè)計(jì)—請(qǐng)求同步
集群建立好之后,可以向其發(fā)送讀寫(xiě)請(qǐng)求。發(fā)送讀請(qǐng)求非常簡(jiǎn)單,client 發(fā)送一個(gè)讀請(qǐng)求給 TuGraph server,server 接到讀請(qǐng)求之后,去進(jìn)行處理。圖中給出的一個(gè) cypher 語(yǔ)句,是查詢(xún)圖中邊的 label 的數(shù)量。在 server 端,會(huì)對(duì) cypher 語(yǔ)句進(jìn)行解析,辨別它是一個(gè)只讀的請(qǐng)求,一旦確定就會(huì)直接發(fā)送給 TuGraph-DB,由 TuGraph-DB 進(jìn)行響應(yīng)。
寫(xiě)請(qǐng)求會(huì)涉及到 DB 數(shù)據(jù)的變化。Client 發(fā)送給 server 之后,server 會(huì)通過(guò)一些自有的邏輯去判斷,如果是一個(gè)寫(xiě)請(qǐng)求,那么它會(huì)傳給內(nèi)部的一個(gè) raft node,這個(gè) raft node 可以看作是一個(gè) client。因?yàn)槭侨齻€(gè)節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)持有其它節(jié)點(diǎn)的一個(gè) client,每個(gè)節(jié)點(diǎn)既是 server,也是 client。當(dāng)收到這個(gè)請(qǐng)求之后,只有 leader 節(jié)點(diǎn)會(huì)處理寫(xiě)請(qǐng)求。它并不會(huì)直接應(yīng)用到 TuGraph-DB 上面,而是先調(diào)用客戶(hù)端把日志去發(fā)送給其它節(jié)點(diǎn),當(dāng)超過(guò)半數(shù)的節(jié)點(diǎn)響應(yīng)之后,才會(huì)應(yīng)用到 TuGraph-DB 內(nèi)部,保證寫(xiě)請(qǐng)求日志的一致性。
在高可用集群使用過(guò)程中,有很多不可預(yù)知的情況,比如正好在應(yīng)用日志的時(shí)候,集群突然掛了或者突然重啟了。即使這種情況發(fā)生的概率非常低,但在大規(guī)模應(yīng)用中仍然有可能發(fā)生。因此,寫(xiě)請(qǐng)求必須是冪等的,請(qǐng)求的 log index 必須是一致的,當(dāng)它應(yīng)用到 DB 里時(shí),不能產(chǎn)生重復(fù)的提交。所以我們?cè)?DB 內(nèi)部持有 log 的 index,當(dāng) client 由于超時(shí)重發(fā)或節(jié)點(diǎn)的狀態(tài)發(fā)生變化而重復(fù)提交時(shí),都不會(huì)對(duì) DB 狀態(tài)產(chǎn)生污染。
3. Server 架構(gòu)設(shè)計(jì)—集群成員變更
接下來(lái)要考慮的是集群成員變更的情況。首先來(lái)看增加節(jié)點(diǎn)。增加的節(jié)點(diǎn)以 follower 的角色加入集群,和啟動(dòng)的命令一致就可以將節(jié)點(diǎn)加入集群。增加節(jié)點(diǎn)時(shí)還需要同步日志。
再來(lái)看刪除節(jié)點(diǎn)的情況。我們?nèi)绻褂?Meta Server 去控制集群成員的變更,那么這個(gè) Meta Server 也要是高可用的。如果這個(gè) Meta Server 發(fā)生了宕機(jī),會(huì)導(dǎo)致整體的不可用。為了簡(jiǎn)化設(shè)計(jì),我們直接讓 leader 去控制集群成員的變更。
要?jiǎng)h除節(jié)點(diǎn)時(shí),可以直接調(diào)用下圖中給出的命令,讓節(jié)點(diǎn)離開(kāi)集群。正常情況下是由 leader 向 follower 發(fā)送心跳,為了維護(hù)集群正常的狀態(tài),follower 也會(huì)向 leader 發(fā)送心跳,讓 leader 感知到它的存在。當(dāng) follower 超過(guò)一定的時(shí)間沒(méi)有給 leader 發(fā)送心跳時(shí),就會(huì)把這個(gè) follower 從集群中去除,從而完成了集群成員的變更。
當(dāng)然需要注意的是,比如有 7 個(gè)節(jié)點(diǎn),不能同時(shí)刪掉 4 個(gè)節(jié)點(diǎn),這就相當(dāng)于有 4 個(gè)節(jié)點(diǎn)宕機(jī)了。
4. Server 架構(gòu)設(shè)計(jì)—快照
前文中提到,加入 follower 時(shí),需要同步日志。但是一個(gè)集群經(jīng)過(guò)長(zhǎng)期的服務(wù),日志一定是非常多的,如果每加入一個(gè) follower,都從一年之前、兩年之前的日志開(kāi)始同步,同步過(guò)來(lái)除了 Append,還需要應(yīng)用到 Server 里面,是非常耗時(shí)的。所以需要定期對(duì)節(jié)點(diǎn)打快照,對(duì)數(shù)據(jù)庫(kù)狀態(tài)做一個(gè)保存。再去加入 follower 時(shí),直接傳輸快照就可以了。
Tugraph-DB 不管是安裝快照還是打快照,都是非??焖俚?。因?yàn)?Tugraph-DB 底層支持 MVCC 多版本,生成快照的時(shí)候并不會(huì)去阻塞讀寫(xiě)請(qǐng)求。
5. Client 架構(gòu)設(shè)計(jì)
以上對(duì) server 端的設(shè)計(jì)進(jìn)行了介紹,接下來(lái)介紹 client 端的設(shè)計(jì)。Client 端最重要的有兩點(diǎn),第一點(diǎn)是 client 必須持有集群中所有節(jié)點(diǎn)的連接,不能只有一個(gè)節(jié)點(diǎn)。既然持有所有節(jié)點(diǎn)的連接,也就是連接池,那么它必須要知道誰(shuí)是 leader,所以 client 必須要支持在連接上這個(gè)集群的時(shí)候,自動(dòng)定位到 leader,通過(guò)這種方式,只需要去指定集群中任意一個(gè)節(jié)點(diǎn)的 URL,就可以連接上整個(gè)集群,并自動(dòng)找到集群中的 leader。
第二個(gè)重要的點(diǎn)就是負(fù)載均衡。因?yàn)?leader 數(shù)據(jù)是最全的,并且只有它能寫(xiě)。但是整個(gè)高可用架構(gòu),并不只提供數(shù)據(jù)的安全性,還要提供讀寫(xiě)的功能,并且能更好的利用硬件資源,所以我們要對(duì)讀請(qǐng)求做負(fù)載均衡,以輪詢(xún)的方式去循環(huán)地發(fā)送給每一個(gè)節(jié)點(diǎn),來(lái)提高訪(fǎng)問(wèn)性能。
6. 未來(lái)規(guī)劃
未來(lái)規(guī)劃,主要有三個(gè)方面。
第一個(gè)是 Witness 角色。這個(gè)角色起源于一個(gè)場(chǎng)景,假如一個(gè)企業(yè)沒(méi)有很多機(jī)器,比如只有兩臺(tái)機(jī)器,但是它又想用高可用的架構(gòu),那應(yīng)該怎么辦?引入 Raft 擴(kuò)展角色,就是 witness 角色,這個(gè)角色有一個(gè)特點(diǎn),它只參與選舉,但是不參與日志復(fù)制。也就是可以在一個(gè)機(jī)器里面運(yùn)行兩個(gè)節(jié)點(diǎn),一個(gè)節(jié)點(diǎn)不復(fù)制日志,只參與選舉。那么在另外一個(gè)機(jī)器里面去運(yùn)行一個(gè)節(jié)點(diǎn),這樣兩臺(tái)機(jī)器就可以啟動(dòng)高可用集群了。這樣不僅可以減少數(shù)據(jù)的傳輸量,而且可以提高可用性和性能,因?yàn)樗挥脩?yīng)用日志了。
第二個(gè)是按需快照。大家在使用圖數(shù)據(jù)庫(kù)的時(shí)候,數(shù)據(jù)量可能是非常龐大的。比如我們內(nèi)部有一個(gè) 1.2T 的線(xiàn)上業(yè)務(wù),當(dāng)它去打 snapshot 的時(shí)候,時(shí)間非常長(zhǎng),大概要兩個(gè)小時(shí)。雖然它并不阻塞讀寫(xiě)請(qǐng)求,但對(duì)性能的影響還是非常嚴(yán)重的,并且會(huì)放大存儲(chǔ)空間,原來(lái)數(shù)據(jù)是 1.2T,打完之后可能就變成 2.4T 了。所以我們希望改造 Raft 協(xié)議,去提供按需生成快照的功能,只有在 follower 加入節(jié)點(diǎn)的時(shí)候才進(jìn)行快照,因?yàn)橹挥?follower 加入這個(gè)節(jié)點(diǎn)時(shí)需要快照來(lái)加速數(shù)據(jù)同步。
第三個(gè)是高可用工具鏈。因?yàn)?TuGraph-DB 的高可用功能是 3.6 版本才開(kāi)源,它提供的工具鏈現(xiàn)在還有一些待完善的地方,比如在線(xiàn)導(dǎo)入,只提供了單機(jī)的功能,我們希望可以提供一個(gè)集群在線(xiàn)導(dǎo)入,這樣就不用去對(duì)多機(jī)執(zhí)行在線(xiàn)導(dǎo)入的功能了。還有快照生成工具,client 的接口完善等等。
三、TuGraph-DB 高可用集群部署與應(yīng)用
1. TuGraph-DB 高可用(V3.6)
關(guān)于 TuGraph-DB 高可用集群的部署方式和 client 應(yīng)用,相關(guān)文檔已經(jīng)放到了 tugraph-db.readthedocs.io 網(wǎng)站上。
現(xiàn)在支持 C++、Java 和 Python 多種版本的 client SDK。
2. 高可用集群部署
當(dāng)原始數(shù)據(jù)一致的時(shí)候,可以直接指定 HA configure 參數(shù)啟動(dòng)集群。當(dāng)初始數(shù)據(jù)不一致的時(shí)候,假如有一個(gè)節(jié)點(diǎn)有數(shù)據(jù),其它節(jié)點(diǎn)沒(méi)有數(shù)據(jù),需要把數(shù)據(jù)同步到其它節(jié)點(diǎn),但是又不能通過(guò) SCP 傳,那么就可以通過(guò)初始數(shù)據(jù)不一致的方式去啟動(dòng)。有數(shù)據(jù)的節(jié)點(diǎn)用 bootstrap 方式啟動(dòng),預(yù)先生成一個(gè)快照,然后其它節(jié)點(diǎn)以 follower 的身份加入集群,加入集群時(shí)會(huì)安裝快照,安裝快照之后才會(huì)進(jìn)行選舉和 follower 身份的確認(rèn)。
3. Client 連接應(yīng)用
啟動(dòng)完集群之后,前端會(huì)有一個(gè)高可用的列表,client 有直接方式和間接方式連接的區(qū)別,主要是考慮到在亞馬遜云部署和阿里云部署的區(qū)別。連接完了之后就可以去執(zhí)行請(qǐng)求,比如執(zhí)行寫(xiě)請(qǐng)求的時(shí)候,要先寫(xiě)到 leader 再同步到 follower,可以看到是有日志同步的。