大(da)家都(dou)知道:由(you)(you)于單機數據庫在數據規模、并發訪問量等方(fang)面存在瓶(ping)頸,無法滿足大(da)規模應用的(de)需求。因此(ci)才有了把數據切割分片,分布存儲分布處理在多(duo)個節點上(shang)的(de)數據庫,也(ye)就是分布式數據庫的(de)由(you)(you)來。
而為了實現數據庫的高可用,又有了多副本的概念,副本之間的數據需要用特定算法保持一致,從而可以隨時切換身份對外提供高可用服務——TDengine 就是一款這樣的分布式時序數據庫(Time Series Database)。
選擇(ze)什么樣的一致(zhi)性或者共識算法(fa),可以直接代表一款(kuan)數據(ju)庫的產品思路。
一. Why RAFT
在 3.0 之前,結(jie)合時(shi)(shi)序(xu)(xu)數(shu)據的十大特點(dian)(),我(wo)們(men)針對性地創造(zao)了(le)多項時(shi)(shi)序(xu)(xu)數(shu)據處(chu)理專利(li),使(shi)得 TDengine 對大時(shi)(shi)序(xu)(xu)數(shu)據的處(chu)理能力(li)與當時(shi)(shi)世界上的同類型產品拉開了(le)巨大的性能優(you)勢(shi)。而在 3.0 ,我(wo)們(men)則引(yin)入了(le)標準的 RAFT 算法,以(yi)更通用標準的實現方式,去適用更廣泛的應(ying)用場景。
通過 RAFT 算法所(suo)保障數據(ju)一致的(de)多個(ge)節點(dian),被(bei)稱(cheng)為一個(ge) RAFT 組(group);這(zhe)些節點(dian),被(bei)稱(cheng)為這(zhe)個(ge)RAFT組的(de)成(cheng)員(yuan)(member)節點(dian)。
在 RAFT 組中,每個(ge)節點都(dou)維(wei)護(hu)了一(yi)份連續的(de)(de)日(ri)(ri)(ri)志(zhi)(Log),用于記錄數(shu)據寫入、變更、或刪除(chu)等操作的(de)(de)所有(you)指令。日(ri)(ri)(ri)志(zhi)是由一(yi)系列有(you)序的(de)(de)日(ri)(ri)(ri)志(zhi)條目 (Log Entry) 組成,每個(ge) Log Entry 都(dou)有(you)唯一(yi)的(de)(de)編號(Index),用于標識(shi)日(ri)(ri)(ri)志(zhi)協商或執行的(de)(de)進度。
此外,每個 RAFT 節點都有(you)自己的角色,它們(men)可以是 Follower(跟隨者)、Candidate(候選人)、Leader(領導者)。

二. Raft in TDengine
對于 TDengine 來(lai)說,一(yi)(yi)個(ge)虛擬節(jie)(jie)點(dian)(dian)組(zu)(vgroup)就構成(cheng)了一(yi)(yi)個(ge) RAFT 組(zu);而這個(ge)虛擬節(jie)(jie)點(dian)(dian)組(zu)的虛擬數據節(jie)(jie)點(dian)(dian)(vnode),便(bian)是該(gai) RAFT 組(zu)的成(cheng)員節(jie)(jie)點(dian)(dian),我們也稱之為(wei)副本(全部(bu)管(guan)理節(jie)(jie)點(dian)(dian)(mnode)也構成(cheng)一(yi)(yi)個(ge) RAFT 組(zu))。Leader 角色(se)的 vnode/mnode 按照協議機制負責(ze)提供讀寫(xie)服(fu)務,在容(rong)忍(ren)故障(zhang)節(jie)(jie)點(dian)(dian)不超(chao)過(guo)半數的情況下(xia)保證集群的高可(ke)用性;此外,即使(shi)發生了節(jie)(jie)點(dian)(dian)重(zhong)(zhong)啟(qi)及 Leader 節(jie)(jie)點(dian)(dian)重(zhong)(zhong)新(xin)選舉等(deng)事件(jian)后,RAFT 也能夠(gou)始(shi)終保證新(xin)產生的 Leader 節(jie)(jie)點(dian)(dian)可(ke)以提供已經寫(xie)入(ru)成(cheng)功的全部(bu)完整數據的讀寫(xie)服(fu)務。
對(dui)于(yu) TDengine 來(lai)說,每(mei)(mei)一次(ci)對(dui)數(shu)據庫(ku)的(de)(de)變更(geng)請求(qiu)( 比如(ru) insert into test.d1 values(now,1,2,3) ),都(dou)對(dui)應一個 RAFT 日志記錄(Log Entry)。在(zai)持續寫入數(shu)據的(de)(de)過程中,TDengine 會按照協議(yi)機制在(zai)每(mei)(mei)個成員(yuan)節(jie)點上產(chan)生完(wan)全相同(tong)的(de)(de)日志記錄,并且以(yi)相同(tong)的(de)(de)順序(xu)執行數(shu)據變更(geng)操作,以(yi) WAL 的(de)(de)形式,存儲(chu)在(zai)數(shu)據文件目錄中。
而每一個 Log Entry 攜帶的(de) Index ,就代表(biao)此前(qian)講過的(de)數據或數據變更的(de)版本(ben)號(hao)()。

當(dang)一個數據寫入請(qing)求發出后,必(bi)定(ding)至少過半數節點上完成寫入才會把“寫入成功(gong)”返回給客戶端(duan)。這部(bu)分涉(she)及 Log entry 的兩種重要的狀態,committed 和 applied。
對應到 TDengine 中(zhong)(zhong),只有當過半數(shu)的(de)(de)節(jie)點把(ba)該條 SQL 的(de)(de)寫入(ru)信息追加到文(wen)件系統上的(de)(de) WAL(即圖(tu)中(zhong)(zhong)的(de)(de)步驟 2),并且收到確認消息之后,這條 Log entry 才會(hui)被 Leader 認為(wei)是安全(quan)的(de)(de)。此時該日志進(jin)入(ru) committed 狀態,并通知 Follower 節(jie)點,完成數(shu)據的(de)(de)插入(ru)(即圖(tu)中(zhong)(zhong)的(de)(de)步驟 4),隨后該 Log Entry 便被標記(ji)為(wei) applied 的(de)(de)狀態。
applied——表示數據變更已(yi)經被應用(yong),即該 SQL 執行(xing)完畢。

數據的(de)整體寫入(ru)流程可以參考官方文檔?。如果需要更多了解 RAFT 細節可以參考論文《 In Search of an Understandable Consensus Algorithm?》
三. 注意事項
對于用戶而言,數(shu)據的(de)(de)同(tong)步方式其實(shi)是無(wu)需(xu)感知的(de)(de),大家只需(xu)要關(guan)心最(zui)終結果即可(ke)。但是關(guan)于選(xuan)主(zhu)流(liu)程大家有必(bi)要了解一(yi)些,因(yin)為(wei)它(ta)涉及(ji)(ji)到(dao)(dao)服務(wu)的(de)(de)壓力負(fu)載是否(fou)均衡,以及(ji)(ji)到(dao)(dao)遇到(dao)(dao)突發(fa)情況后解決(jue)問題的(de)(de)思路。
在 RAFT 的(de)標準實現下,每(mei)個(ge)節點(dian)都(dou)擁有(you)隨機(ji)的(de)選舉(ju)超時時間(jian)(election timeout),當任意一個(ge) Follower 超過這個(ge)時間(jian)沒有(you)收到 Leader 發(fa)來(lai)的(de)心跳后,便會變成 Candidate 并發(fa)起申請成為 Leader 的(de)選舉(ju)請求。
可見,每一個 Vgroup 的選舉結果都完全隨機的,所以這樣會低概率出現很多 Leader Vnode 集中在某一個節點的場景,比如在集群啟動時、滾動升級時(企業版)。由于 RAFT 的讀寫全部只能由 Leader 節點來提供(2.0 版本 TDengine 的 Follower 也可提供查詢服務),因(yin)此這會導致(zhi)單(dan)個節點的工作壓(ya)力較大(da)。
為(wei)了(le)優化(hua)這種特殊情況(kuang),企業版 TDengine 提供了(le)一種簡(jian)便易(yi)用的命令,能夠使集群的負(fu)載(zai)再次實現均衡(heng)。
balance vgroup Leader;
開源版則可以通過重啟服務重新選主刷新選舉結果。(相比 2.0 版(ban)本,優化后(hou)的 3.0 即便是體量(liang)很大的集群也可以迅(xun)速啟停)
此外(wai),由于 RAFT 的選舉流程規(gui)定只需得到半(ban)數(shu)以(yi)(yi)上(shang)節(jie)點的投票就可(ke)以(yi)(yi)選出(chu) Leader,所(suo)以(yi)(yi)在 3.0 版本,原則上(shang)講,只需要半(ban)數(shu)以(yi)(yi)上(shang)節(jie)點正常便可(ke)以(yi)(yi)提供(gong)(gong)服務(wu)。(2.0 需要節(jie)點全部啟動才可(ke)從無到有選出(chu) Leader ,后續(xu)才會按照半(ban)數(shu)以(yi)(yi)上(shang)的多數(shu)派原則提供(gong)(gong)高可(ke)用服務(wu))

四. 常見問題:
問題 1 :“Sync leader is unreachable”(3.0.3.1 版本之前(qian),該錯誤碼為 sync not Leader )
該報(bao)錯的具體(ti)邏輯是(shi):由(you)于 RAFT 要求(qiu)(qiu)只有(you) Leader 才可以提(ti)供讀(du)寫(xie)服務,一個請求(qiu)(qiu)發現自己訪問的 vnode/mnode 不是(shi) Leader 后,會(hui)把請求(qiu)(qiu)重(zhong)定向到(dao)新的 vnode/mnode 上 。 如果最終還是(shi)沒(mei)有(you)找到(dao)Leader 才會(hui)返(fan)回客戶端錯誤: “Sync leader is unreachable ”,所以本質上這個報(bao)錯是(shi)由(you)于 Leader 不存在導致(zhi)的。

問題現象及處理(li)思路(lu):
- 數據庫/mnode 在沒有設置 3 副本的情況下出現故障:這時需要打開debugFlag 143 , 生成高級別日志,通過日志信息判斷問題所在,或者檢查硬件。
- 多副本情況無法選出 Leader :同樣需要打開debugFlag 143 , 生成高級別日志,通過日志信息判斷問題所在,或者檢查硬件。
- 數據庫主程序出現bug導致程序無法啟動,因此無法選出 Leader :這種情況需要通過微信群或 github 聯系官方團隊協助處理。
除此(ci)之(zhi)外,但凡在日志中發現(xian) “not leader ” 相關字樣的報錯信息,都(dou)可以按(an)照上述思路排查(cha)。
常見問題 2 : Sync leader is restoring。

該報錯的(de)(de)(de)具體邏輯是(shi):假如(ru)數據庫(ku)節點(dian)出(chu)現了斷電,或(huo)其他(ta)原因導致數據庫(ku)進程異(yi)常中(zhong)止。這(zhe)樣內存中(zhong)的(de)(de)(de)數便會被清(qing)空,數據庫(ku)需要把已(yi)經寫入磁盤的(de)(de)(de) WAL 數據再次執行,使內存恢復到異(yi)常發生(sheng)之(zhi)前(qian)的(de)(de)(de)狀態。(上(shang)圖紅色箭頭部分)
這(zhe)個時候,該 vnode 便處于 restoring 的狀態,需要完全(quan)恢復后才可以(yi)正常提供(gong)服(fu)務(wu),在恢復完成之前如果訪問了這(zhe)個 vnode 數據就會報錯 “Sync leader is restoring”。
通(tong)常(chang)我(wo)們不認為這是一(yi)個異常(chang)情(qing)況,而(er)是數(shu)據庫的正常(chang)恢復行為,此時我(wo)們可(ke)以(yi)觀(guan)察日志(zhi)中該條輸出(chu)的 “items”,直(zhi)到歸 0 即(ji)可(ke)恢復正常(chang)使用,如果恢復較慢,或(huo)中途出(chu)現其它問題,通(tong)過(guo)微(wei)信群或(huo) github 聯(lian)系我(wo)們官方即(ji)可(ke)。

五. 結語
RAFT 的(de)引入(ru)是 3.0 大(da)規(gui)模重構中最(zui)大(da)的(de)亮(liang)點(dian)之一,在保住了 TDengine 的(de)性能優勢的(de)前(qian)提下(xia),使(shi)得 TDengine 能以更標(biao)準(zhun)的(de)方式處理數據(ju)的(de)一致(zhi)性問題(ti)。
在存儲(chu)引擎(qing),一(yi)致性協(xie)議,查(cha)詢引擎(qing)全部做(zuo)了大(da)(da)規模重構之后,TDengine 3.0 將會在更廣泛的時序數據(ju)處(chu)理場(chang)景中發(fa)揮作用,歡迎(ying)大(da)(da)家加入(ru)我們(men)一(yi)起探索可能。


























