六月婷婷AV,国产偷窥猎奇福利二区,日韩三级片。,好吊色网站,日韩成人中文在线视频,国产亚洲午夜啪啪,亚洲欧美另类国产精品,国产成人av1,任你艹在线观看

TDengine 2.0 數(shù)據(jù)復(fù)制模塊設(shè)計(jì)

數(shù)據(jù)復(fù)制概述

數(shù)據(jù)復(fù)制(Replication)是指同一份數(shù)據(jù)在多個(gè)物理地點(diǎn)保存。它的目的是防止數(shù)據(jù)丟失,提高系統(tǒng)的高可用性(High Availability),而且通過應(yīng)用訪問多個(gè)副本,提升數(shù)據(jù)查詢性能。

在高可靠的大數(shù)據(jù)系統(tǒng)里,數(shù)據(jù)復(fù)制是必不可少的一大功能。數(shù)據(jù)復(fù)制又分為實(shí)時(shí)復(fù)制與非實(shí)時(shí)復(fù)制。實(shí)時(shí)復(fù)制是指任何數(shù)據(jù)的更新(包括數(shù)據(jù)的增加、刪除、修改)操作,會(huì)被實(shí)時(shí)的復(fù)制到所有副本,這樣任何一臺(tái)機(jī)器宕機(jī)或網(wǎng)絡(luò)出現(xiàn)故障,整個(gè)系統(tǒng)還能提供最新的數(shù)據(jù),保證系統(tǒng)的正常工作。而非實(shí)時(shí)復(fù)制,是指傳統(tǒng)的數(shù)據(jù)備份操作,按照固定的時(shí)間周期,將一份數(shù)據(jù)全量或增量復(fù)制到其他地方。如果主節(jié)點(diǎn)宕機(jī),副本是很大可能沒有最新數(shù)據(jù),因此在有些場景是無法滿足要求的。

TDengine面向的是物聯(lián)網(wǎng)場景,需要支持?jǐn)?shù)據(jù)的實(shí)時(shí)復(fù)制,來最大程度保證系統(tǒng)的可靠性。實(shí)時(shí)復(fù)制有兩種方式,一種是異步復(fù)制,一種是同步復(fù)制。異步復(fù)制(Asynchronous Replication)是指數(shù)據(jù)由Master轉(zhuǎn)發(fā)給Slave后,Master并不需要等待Slave回復(fù)確認(rèn),這種方式效率高,但有極小的概率會(huì)丟失數(shù)據(jù)。同步復(fù)制是指Master將數(shù)據(jù)轉(zhuǎn)發(fā)給Slave后,需要等待Slave的回復(fù)確認(rèn),才會(huì)通知應(yīng)用寫入成功,這種方式效率偏低,但能保證數(shù)據(jù)絕不丟失。

數(shù)據(jù)復(fù)制是與數(shù)據(jù)存儲(chǔ)(寫入、讀?。┟芮邢嚓P(guān)的,但兩者又是相對獨(dú)立,可以完全脫耦的。在TDengine系統(tǒng)中,有兩種不同類型的數(shù)據(jù),一種是時(shí)序數(shù)據(jù),由TSDB模塊負(fù)責(zé);一種是元數(shù)據(jù)(Meta Data), 由MNODE負(fù)責(zé)。這兩種性質(zhì)不同的數(shù)據(jù)都需要同步功能。數(shù)據(jù)復(fù)制模塊通過不同的實(shí)例啟動(dòng)配置參數(shù),為這兩種類型數(shù)據(jù)都提供同步功能。

在閱讀本文之前,請先閱讀《TDengine 2.0 整體架構(gòu)》,了解TDengine的集群設(shè)計(jì)和基本概念

特別注明:本文中提到數(shù)據(jù)更新操作包括數(shù)據(jù)的增加、刪除與修改。

基本概念和定義

TDengine里存在vnode, mnode, vnode用來存儲(chǔ)時(shí)序數(shù)據(jù),mnode用來存儲(chǔ)元數(shù)據(jù)。但從同步數(shù)據(jù)復(fù)制的模塊來看,兩者沒有本質(zhì)的區(qū)別,因此本文里的虛擬節(jié)點(diǎn)不僅包括vnode, 也包括mnode, vgroup也指mnode group, 除非特別注明。

版本(version)

一個(gè)虛擬節(jié)點(diǎn)組里多個(gè)虛擬節(jié)點(diǎn)互為備份,來保證數(shù)據(jù)的有效與可靠,是依靠虛擬節(jié)點(diǎn)組的數(shù)據(jù)版本號(hào)來維持的。TDengine2.0設(shè)計(jì)里,對于版本的定義如下:客戶端發(fā)起增加、刪除、修改的流程,無論是一條記錄還是多條,只要是在一個(gè)請求里,這個(gè)數(shù)據(jù)更新請求被TDengine的一個(gè)虛擬節(jié)點(diǎn)收到后,經(jīng)過合法性檢查后,可以被寫入系統(tǒng)時(shí),就會(huì)被分配一個(gè)版本號(hào)。這個(gè)版本號(hào)在一個(gè)虛擬節(jié)點(diǎn)里從1開始,是單調(diào)連續(xù)遞增的。無論這條記錄是采集的時(shí)序數(shù)據(jù)還是meta data, 一樣處理。當(dāng)Master轉(zhuǎn)發(fā)一個(gè)寫入請求到slave時(shí),必須帶上版本號(hào)。一個(gè)虛擬節(jié)點(diǎn)將一數(shù)據(jù)更新請求寫入WAL時(shí),需要帶上版本號(hào)。

不同虛擬節(jié)點(diǎn)組的數(shù)據(jù)版本號(hào)是完全獨(dú)立的,互不相干的。版本號(hào)本質(zhì)上是數(shù)據(jù)更新記錄的transaction ID,但用來標(biāo)識(shí)數(shù)據(jù)集的版本。

角色(role):

一個(gè)虛擬節(jié)點(diǎn)可以是master, slave, unsynced或offline狀態(tài)。

  • master: 具有最新的數(shù)據(jù),容許客戶端往里寫入數(shù)據(jù),一個(gè)虛擬節(jié)點(diǎn)組,至多一個(gè)master.
  • slave:與master是同步的,但不容許客戶端往里寫入數(shù)據(jù),根據(jù)配置,可以容許客戶端對其進(jìn)行查詢。
  • unsynced: 節(jié)點(diǎn)處于非同步狀態(tài),比如虛擬節(jié)點(diǎn)剛啟動(dòng)、或與其他虛擬節(jié)點(diǎn)的連接出現(xiàn)故障等。處于該狀態(tài)時(shí),該虛擬節(jié)點(diǎn)既不能提供寫入,也不能提供查詢服務(wù)。
  • offline: 由于宕機(jī)或網(wǎng)絡(luò)原因,無法訪問到某虛擬節(jié)點(diǎn)時(shí),其他虛擬節(jié)點(diǎn)將該虛擬節(jié)點(diǎn)標(biāo)為離線。但請注意,該虛擬節(jié)點(diǎn)本身的狀態(tài)可能是unsynced或其他,但不會(huì)是離線。

Quorum:

指數(shù)據(jù)寫入成功所需要的確認(rèn)數(shù)。對于異步復(fù)制,quorum設(shè)為1,具有master角色的虛擬節(jié)點(diǎn)自己確認(rèn)即可。對于同步復(fù)制,需要至少大于等于2。原則上,Quorum >=1 并且 Quorum <= replication(副本數(shù))。這個(gè)參數(shù)在啟動(dòng)一個(gè)同步模塊實(shí)例時(shí)需要提供。

WAL:

TDengine的WAL(Write Ahead Log)與cassandra的commit log, mySQL的bin log, Postgres的WAL沒本質(zhì)區(qū)別。沒有寫入數(shù)據(jù)庫文件,還保存在內(nèi)存的數(shù)據(jù)都會(huì)先存在WAL。當(dāng)數(shù)據(jù)已經(jīng)成功寫入數(shù)據(jù)庫數(shù)據(jù)文件,相應(yīng)的WAL會(huì)被刪除。但需要特別指明的是,在TDengine系統(tǒng)里,有幾點(diǎn):

  • 每個(gè)虛擬節(jié)點(diǎn)有自己獨(dú)立的wal
  • WAL里包含而且僅僅包含來自客戶端的數(shù)據(jù)更新操作,每個(gè)更新操作都會(huì)被打上一個(gè)版本號(hào)

復(fù)制實(shí)例:

復(fù)制模塊只是一可執(zhí)行的代碼,復(fù)制實(shí)例是指正在運(yùn)行的復(fù)制模塊的一個(gè)實(shí)例,一個(gè)節(jié)點(diǎn)里,可以存在多個(gè)實(shí)例。原則上,一個(gè)節(jié)點(diǎn)有多少虛擬節(jié)點(diǎn),就可以啟動(dòng)多少實(shí)例。對于副本數(shù)為1的場景,應(yīng)用可以決定是否需要啟動(dòng)同步實(shí)例。應(yīng)用啟動(dòng)一個(gè)同步模塊的實(shí)例時(shí),需要提供的就是虛擬節(jié)點(diǎn)組的配置信息,包括:

  • 虛擬節(jié)點(diǎn)個(gè)數(shù),即replication number
  • 各虛擬節(jié)點(diǎn)所在節(jié)點(diǎn)的信息,包括node的end point
  • quorum, 需要的數(shù)據(jù)寫入成功的確認(rèn)數(shù)
  • 虛擬節(jié)點(diǎn)的初始版本號(hào)

數(shù)據(jù)復(fù)制模塊的基本工作原理

TDengine采取的是Master-Slave模式進(jìn)行同步,與流行的RAFT一致性算法比較一致??偨Y(jié)下來,有幾點(diǎn):

  1. 一個(gè)vgroup里有一到多個(gè)虛擬節(jié)點(diǎn),每個(gè)虛擬節(jié)點(diǎn)都有自己的角色
  2. 客戶端只能向角色是master的虛擬節(jié)點(diǎn)發(fā)起數(shù)據(jù)更新操作,因?yàn)閙aster具有最新版本的數(shù)據(jù),如果向非Master發(fā)起數(shù)據(jù)更新操作,會(huì)直接收到錯(cuò)誤
  3. 客戶端可以向master, 也可以向角色是Slave的虛擬節(jié)點(diǎn)發(fā)起查詢操作,但不能對unsynced的虛擬節(jié)點(diǎn)發(fā)起任何操作
  4. 如果master不存在,這個(gè)vgroup是不能對外提供數(shù)據(jù)更新和查詢服務(wù)的
  5. master收到客戶端的數(shù)據(jù)更新操作時(shí),會(huì)將其轉(zhuǎn)發(fā)給slave節(jié)點(diǎn)
  6. 一個(gè)虛擬節(jié)點(diǎn)的版本號(hào)比master低的時(shí)候,會(huì)發(fā)起數(shù)據(jù)恢復(fù)流程,成功后,才會(huì)成為slave

數(shù)據(jù)實(shí)時(shí)復(fù)制有三個(gè)主要流程:選主、數(shù)據(jù)轉(zhuǎn)發(fā)、數(shù)據(jù)恢復(fù)。后續(xù)做詳細(xì)討論。

虛擬節(jié)點(diǎn)之間的網(wǎng)絡(luò)連接

虛擬節(jié)點(diǎn)之間通過TCP進(jìn)行連接,節(jié)點(diǎn)之間的狀態(tài)交換、數(shù)據(jù)包的轉(zhuǎn)發(fā)都是通過這個(gè)TCP連接(peerFd)進(jìn)行。為避免競爭,兩個(gè)虛擬節(jié)點(diǎn)之間的TCP連接,總是由IP地址(UINT32)小的節(jié)點(diǎn)作為TCP客戶端發(fā)起。一旦TCP連接被中斷,虛擬節(jié)點(diǎn)能通過TCP socket自動(dòng)檢測到,將對方標(biāo)為offline。如果監(jiān)測到任何錯(cuò)誤(比如數(shù)據(jù)恢復(fù)流程),虛擬節(jié)點(diǎn)將主動(dòng)重置該連接。

一旦作為客戶端的節(jié)點(diǎn)連接不成或中斷,它將周期性的每隔一秒鐘去試圖去連接一次。因?yàn)門CP本身有心跳機(jī)制,虛擬節(jié)點(diǎn)之間不再另行提供心跳。

如果一個(gè)unsynced節(jié)點(diǎn)要發(fā)起數(shù)據(jù)恢復(fù)流程,它與Master將建立起專有的TCP連接(syncFd)。數(shù)據(jù)恢復(fù)完成后,該連接會(huì)被關(guān)閉。而且為限制資源的使用,系統(tǒng)只容許一定數(shù)量(配置參數(shù)tsMaxSyncNum)的數(shù)據(jù)恢復(fù)的socket存在。如果超過這個(gè)數(shù)字,系統(tǒng)會(huì)將新的數(shù)據(jù)恢復(fù)請求延后處理。

任意一個(gè)節(jié)點(diǎn),無論有多少虛擬節(jié)點(diǎn),都會(huì)啟動(dòng)而且只會(huì)啟動(dòng)一個(gè)TCP server, 來接受來自其他虛擬節(jié)點(diǎn)的上述兩類TCP的連接請求。當(dāng)TCP socket建立起來,客戶端側(cè)發(fā)送的消息體里會(huì)帶有vgId(全局唯一的vgroup ID), TCP 服務(wù)器側(cè)會(huì)檢查該vgId是否已經(jīng)在該節(jié)點(diǎn)啟動(dòng)運(yùn)行。如果已經(jīng)啟動(dòng)運(yùn)行,就接受其請求。如果不存在,就直接將連接請求關(guān)閉。在TDengine代碼里,mnode group的vgId設(shè)置為1。

選主流程

當(dāng)同一組的兩個(gè)虛擬節(jié)點(diǎn)之間(vnode A, vnode B)建立連接后,他們互換status消息。status消息里包含本地存儲(chǔ)的同一虛擬節(jié)點(diǎn)組內(nèi)所有虛擬節(jié)點(diǎn)的role和version。

如果一個(gè)虛擬節(jié)點(diǎn)(vnode A)檢測到與同一虛擬節(jié)點(diǎn)組內(nèi)另外一虛擬節(jié)點(diǎn)(vnode B)的連接中斷,vnode A將立即把vnode B的role設(shè)置為offline。無論是接收到另外一虛擬節(jié)點(diǎn)發(fā)來的status消息,還是檢測與另外一虛擬節(jié)點(diǎn)的連接中斷,該虛擬節(jié)點(diǎn)都將進(jìn)入狀態(tài)處理流程。狀態(tài)處理流程的規(guī)則如下:

  1. 如果檢測到在線的節(jié)點(diǎn)數(shù)沒有超過一半,則將自己的狀態(tài)設(shè)置為unsynced.
  2. 如果在線的虛擬節(jié)點(diǎn)數(shù)超過一半,會(huì)檢查master節(jié)點(diǎn)是否存在,如果存在,則會(huì)決定是否將自己狀態(tài)改為slave或啟動(dòng)數(shù)據(jù)恢復(fù)流程。
  3. 如果master不存在,則會(huì)檢查自己保存的各虛擬節(jié)點(diǎn)的狀態(tài)信息與從另一節(jié)點(diǎn)接收到的是否一致,如果一致,說明節(jié)點(diǎn)組里狀態(tài)已經(jīng)穩(wěn)定一致,則會(huì)觸發(fā)選舉流程。如果不一致,說明狀態(tài)還沒趨于一致,即使master不存在,也不進(jìn)行選主。由于要求狀態(tài)信息一致才進(jìn)行選舉,每個(gè)虛擬節(jié)點(diǎn)根據(jù)同樣的信息,會(huì)選出同一個(gè)虛擬節(jié)點(diǎn)做master,無需投票表決。
  4. 自己的狀態(tài)是根據(jù)規(guī)則自己決定并修改的,并不需要其他節(jié)點(diǎn)同意,包括成為master。一個(gè)節(jié)點(diǎn)無權(quán)修改其他節(jié)點(diǎn)的狀態(tài)。
  5. 如果一個(gè)虛擬節(jié)點(diǎn)檢測到自己或其他虛擬節(jié)點(diǎn)的role發(fā)生改變,該節(jié)點(diǎn)會(huì)廣播它自己保存的各個(gè)虛擬節(jié)點(diǎn)的狀態(tài)信息(role和version)。

具體的流程圖如下:

replica-master.png

選擇Master的具體規(guī)則如下:

  1. 如果只有一個(gè)副本,該副本永遠(yuǎn)就是master
  2. 所有副本都在線時(shí),版本最高的被選為master
  3. 在線的虛擬節(jié)點(diǎn)數(shù)過半,而且有虛擬節(jié)點(diǎn)是slave的話,該虛擬節(jié)點(diǎn)自動(dòng)成為master
  4. 對于2和3,如果多個(gè)虛擬節(jié)點(diǎn)滿足成為master的要求,那么虛擬節(jié)點(diǎn)組的節(jié)點(diǎn)列表里,最前面的選為master

按照上面的規(guī)則,如果所有虛擬節(jié)點(diǎn)都是unsynced(比如全部重啟),只有所有虛擬節(jié)點(diǎn)上線,才能選出master,該虛擬節(jié)點(diǎn)組才能開始對外提供服務(wù)。當(dāng)一個(gè)虛擬節(jié)點(diǎn)的role發(fā)生改變時(shí),sync模塊回通過回調(diào)函數(shù)notifyRole通知應(yīng)用。

數(shù)據(jù)轉(zhuǎn)發(fā)流程

如果vnode A是master, vnode B是slave, vnode A能接受客戶端的寫請求,而vnode B不能。當(dāng)vnode A收到寫的請求后,遵循下面的流程:

replica-forward.png

  1. 應(yīng)用對寫請求做基本的合法性檢查,通過,則給該請求包打上一個(gè)版本號(hào)(version, 單調(diào)遞增)
  2. 應(yīng)用將打上版本號(hào)的寫請求封裝一個(gè)WAL Head, 寫入WAL(Write Ahead Log)
  3. 應(yīng)用調(diào)用API syncForwardToPeer,如果vnode B是slave狀態(tài),sync模塊將包含WAL Head的數(shù)據(jù)包通過Forward消息發(fā)送給vnode B,否則就不轉(zhuǎn)發(fā)。
  4. vnode B收到Forward消息后,調(diào)用回調(diào)函數(shù)writeToCache, 交給應(yīng)用處理
  5. vnode B應(yīng)用在寫入成功后,都需要調(diào)用syncConfirmForward通知sync模塊已經(jīng)寫入成功。
  6. 如果quorum大于1,vnode B需要等待應(yīng)用的回復(fù)確認(rèn),收到確認(rèn)后,vnode B發(fā)送Forward Response消息給node A。
  7. 如果quorum大于1,vnode A需要等待vnode B或其他副本對Forward消息的確認(rèn)。
  8. 如果quorum大于1,vnode A收到quorum-1條確認(rèn)消息后,調(diào)用回調(diào)函數(shù)confirmForward,通知應(yīng)用寫入成功。
  9. 如果quorum為1,上述6,7,8步不會(huì)發(fā)生。
  10. 如果要等待slave的確認(rèn),master會(huì)啟動(dòng)2秒的定時(shí)器(可配置),如果超時(shí),則認(rèn)為失敗。

對于回復(fù)確認(rèn),sync模塊提供的是異步回調(diào)函數(shù),因此APP在調(diào)用syncForwardToPeer之后,無需等待,可以處理下一個(gè)操作。在Master與Slave的TCP連接管道里,可能有多個(gè)Forward消息,這些消息是嚴(yán)格按照應(yīng)用提供的順序排好的。對于Forward Response也是一樣,TCP管道里存在多個(gè),但都是排序好的。這個(gè)順序,SYNC模塊并沒有做特別的事情,是由APP單線程順序?qū)憗肀WC的(TDengine里每個(gè)vnode的寫數(shù)據(jù),都是單線程)。

數(shù)據(jù)恢復(fù)流程

如果一虛擬節(jié)點(diǎn)(vnode B) 處于unsynced狀態(tài),master存在(vnode A),而且其版本號(hào)比master的低,它將立即啟動(dòng)數(shù)據(jù)恢復(fù)流程。在理解恢復(fù)流程時(shí),需要澄清幾個(gè)關(guān)于文件的概念和處理規(guī)則。

  1. 每個(gè)文件(無論是archived data的file還是wal)都有一個(gè)index, 這需要應(yīng)用來維護(hù)(vnode里,該index就是fileId*3 + 0/1/2, 對應(yīng)data, head與last三個(gè)文件)。如果index為0,表示系統(tǒng)里最老的數(shù)據(jù)文件。對于mode里的文件,數(shù)量是固定的,對應(yīng)于acct, user, db, table等文件。
  2. 任何一個(gè)數(shù)據(jù)文件(file)有名字、大小,還有一個(gè)magic number。只有文件名、大小與magic number一致時(shí),兩個(gè)文件才判斷是一樣的,無需同步。Magic number可以是checksum, 也可以是簡單的文件大小。怎么計(jì)算magic,換句話說,如何檢測數(shù)據(jù)文件是否有效,完全由應(yīng)用決定。
  3. 文件名的處理有點(diǎn)復(fù)雜,因?yàn)槊颗_(tái)服務(wù)器的路徑可能不一致。比如node A的TDengine的數(shù)據(jù)文件存放在 /etc/taos目錄下,而node B的數(shù)據(jù)存放在 /home/jhtao目錄下。因此同步模塊需要應(yīng)用在啟動(dòng)一個(gè)同步實(shí)例時(shí)提供一個(gè)path,這樣兩臺(tái)服務(wù)器的絕對路徑可以不一樣,但仍然可以做對比,做同步。
  4. 當(dāng)sync模塊調(diào)用回調(diào)函數(shù)getFileInfo獲得數(shù)據(jù)文件信息時(shí),有如下的規(guī)則
    • index 為0,表示獲取最老的文件,同時(shí)修改index返回給sync模塊。如果index不為0,表示獲取指定位置的文件。
    • 如果name為空,表示sync想獲取位于index位置的文件信息,包括magic, size。Master節(jié)點(diǎn)會(huì)這么調(diào)用
    • 如果name不為空,表示sync想獲取指定文件名和index的信息,slave節(jié)點(diǎn)會(huì)這么調(diào)用
    • 如果某個(gè)index的文件不存在,magic返回0,表示文件已經(jīng)是最后一個(gè)。因此整個(gè)系統(tǒng)里,文件的index必須是連續(xù)的一段整數(shù)。
  5. 當(dāng)sync模塊調(diào)用回調(diào)函數(shù)getWalInfo獲得wal信息時(shí),有如下規(guī)則
    • index為0,表示獲得最老的WAL文件, 返回時(shí),index更新為具體的數(shù)字
    • 如果返回0,表示這是最新的一個(gè)WAL文件,如果返回值是1,表示后面還有更新的WAL文件
    • 返回的文件名為空,那表示沒有WAL文件
  6. 無論是getFileInfo, 還是getWalInfo, 只要獲取出錯(cuò)(不是文件不存在),返回-1即可,系統(tǒng)會(huì)報(bào)錯(cuò),停止同步

整個(gè)數(shù)據(jù)恢復(fù)流程分為兩大步驟,第一步,先恢復(fù)archived data(file), 然后恢復(fù)wal。具體流程如下:

replica-restore.png

  1. 通過已經(jīng)建立的TCP連接,發(fā)送sync req給master節(jié)點(diǎn)
  2. master收到sync req后,以client的身份,向vnode B主動(dòng)建立一新的專用于同步的TCP連接(syncFd)
  3. 新的TCP連接建立成功后,master將開始retrieve流程,對應(yīng)的,vnode B將同步啟動(dòng)restore流程
  4. Retrieve/Restore流程里,先處理所有archived data (vnode里的data, head, last文件),后處理WAL data。
  5. 對于archived data,master將通過回調(diào)函數(shù)getFileInfo獲取數(shù)據(jù)文件的基本信息,包括文件名、magic以及文件大小。
  6. master 將獲得的文件名、magic以及文件大小發(fā)給vnode B
  7. vnode B將回調(diào)函數(shù)getFile獲得magic和文件大小,如果兩者一致,就認(rèn)為無需同步,如果兩者不一致 ,就認(rèn)為需要同步。vnode B將結(jié)果通過消息FileAck發(fā)回master
  8. 如果文件需要同步,master就調(diào)用sendfile把整個(gè)文件發(fā)往vnode B
  9. 如果文件不需要同步,master(vnode A)就重復(fù)5,6,7,8,直到所有文件被處理完

對于WAL同步,流程如下:

  1. master節(jié)點(diǎn)調(diào)用回調(diào)函數(shù)getWalInfo,獲取WAL的文件名。
  2. 如果getWalInfo返回值大于0,表示該文件還不是最后一個(gè)WAL,因此master調(diào)用sendfile一下把該文件發(fā)送給vnode B
  3. 如果getWalInfo返回時(shí)為0,表示該文件是最后一個(gè)WAL,因?yàn)槲募赡苓€處于寫的狀態(tài)中,sync模塊要根據(jù)WAL Head的定義逐條讀出記錄,然后發(fā)往vnode B。
  4. vnode A讀取TCP連接傳來的數(shù)據(jù),按照WAL Head,逐條讀取,如果版本號(hào)比現(xiàn)有的大,調(diào)用回調(diào)函數(shù)writeToCache,交給應(yīng)用處理。如果小,直接扔掉。
  5. 上述流程循環(huán),直到所有WAL文件都被處理完。處理完后,master就會(huì)將新來的數(shù)據(jù)包通過Forward消息轉(zhuǎn)發(fā)給slave。

從同步文件啟動(dòng)起,sync模塊會(huì)通過inotify監(jiān)控所有處理過的file以及wal。一旦發(fā)現(xiàn)被處理過的文件有更新變化,同步流程將中止,會(huì)重新啟動(dòng)。因?yàn)橛锌赡苈浔P操作正在進(jìn)行(比如歷史數(shù)據(jù)導(dǎo)入,內(nèi)存數(shù)據(jù)落盤),把已經(jīng)處理過的文件進(jìn)行了修改,需要重新同步才行。

對于最后一個(gè)WAL (LastWal)的處理邏輯有點(diǎn)復(fù)雜,因?yàn)檫@個(gè)文件往往是打開寫的狀態(tài),有很多場景需要考慮,比如:

  • LastWal文件size在增長,需要重新讀;
  • LastWal文件雖然已經(jīng)打開寫,但內(nèi)容為空;
  • LastWal文件已經(jīng)被關(guān)閉,應(yīng)用生成了新的Last WAL文件;
  • LastWal文件沒有被關(guān)閉,但數(shù)據(jù)落盤的原因,沒有讀到完整的一條記錄;
  • LastWal文件沒有被關(guān)閉,但數(shù)據(jù)落盤的原因,還有部分記錄暫時(shí)讀取不到;

sync模塊通過inotify監(jiān)控LastWal文件的更新和關(guān)閉操作。而且在確認(rèn)已經(jīng)盡可能讀完LastWal的數(shù)據(jù)后,會(huì)將對方同步狀態(tài)設(shè)置為SYNC_CACHE。該狀態(tài)下,master節(jié)點(diǎn)會(huì)將新的記錄轉(zhuǎn)發(fā)給vnode B,而此時(shí)vnode B并沒有完成同步,需要把這些轉(zhuǎn)發(fā)包先存在recv buffer里,等WAL處理完后,vnode A再把recv buffer里的數(shù)據(jù)包通過回調(diào)writeToCache交給應(yīng)用處理。

等vnode B把這些buffered forwards處理完,同步流程才算結(jié)束,vnode B正式變?yōu)閟lave。

Master分布均勻性問題

因?yàn)镸aster負(fù)責(zé)寫、轉(zhuǎn)發(fā),消耗的資源會(huì)更多,因此Master在整個(gè)集群里分布均勻比較理想。

但在TDengine的設(shè)計(jì)里,如果多個(gè)虛擬節(jié)點(diǎn)都符合master條件,TDengine選在列表中最前面的做Master, 這樣是否導(dǎo)致在集群里,Master數(shù)量的分布不均勻問題呢?這取決于應(yīng)用的設(shè)計(jì)。

給一個(gè)具體例子,系統(tǒng)里僅僅有三個(gè)節(jié)點(diǎn),IP地址分別為IP1, IP2, IP3. 在各個(gè)節(jié)點(diǎn)上,TDengine創(chuàng)建了多個(gè)虛擬節(jié)點(diǎn)組,每個(gè)虛擬節(jié)點(diǎn)組都有三個(gè)副本。如果三個(gè)副本的順序在所有虛擬節(jié)點(diǎn)組里都是IP1, IP2, IP3, 那毫無疑問,master將集中在IP1這個(gè)節(jié)點(diǎn),這是我們不想看到的。

但是,如果在創(chuàng)建虛擬節(jié)點(diǎn)組時(shí),增加隨機(jī)性,這個(gè)問題就不存在了。比如在vgroup 1, 順序是IP1, IP2, IP3, 在vgroup 2里,順序是IP2, IP3, IP1, 在vgroup 3里,順序是IP3, IP1, IP2。最后master的分布會(huì)是均勻的。

因此在創(chuàng)建一個(gè)虛擬節(jié)點(diǎn)組時(shí),應(yīng)用需要保證節(jié)點(diǎn)的順序是round robin或完全隨機(jī)。

少數(shù)虛擬節(jié)點(diǎn)寫入成功的問題

在某種情況下,寫入成功的確認(rèn)數(shù)大于0,但小于配置的Quorum, 雖然有虛擬節(jié)點(diǎn)數(shù)據(jù)更新成功,master仍然會(huì)認(rèn)為數(shù)據(jù)更新失敗,并通知客戶端寫入失敗。

這個(gè)時(shí)候,系統(tǒng)存在數(shù)據(jù)不一致的問題,因?yàn)橛械奶摂M節(jié)點(diǎn)已經(jīng)寫入成功,而有的寫入失敗。一個(gè)處理方式是,Master重置(reset)與其他虛擬節(jié)點(diǎn)的連接,該虛擬節(jié)點(diǎn)組將自動(dòng)進(jìn)入選舉流程。按照規(guī)則,已經(jīng)成功寫入數(shù)據(jù)的虛擬節(jié)點(diǎn)將成為新的master,組內(nèi)的其他虛擬節(jié)點(diǎn)將從master那里恢復(fù)數(shù)據(jù)。

因?yàn)閷懭胧?,客戶端?huì)重新寫入數(shù)據(jù)。但對于TDengine而言,是OK的。因?yàn)闀r(shí)序數(shù)據(jù)都是有時(shí)間戳的,時(shí)間戳相同的數(shù)據(jù)更新操作,第一次會(huì)執(zhí)行,但第二次會(huì)自動(dòng)扔掉。對于Meta Data(增加、刪除庫、表等等)的操作,也是OK的。一張表、庫已經(jīng)被創(chuàng)建或刪除,再創(chuàng)建或刪除,不會(huì)被執(zhí)行的。

在TDengine的設(shè)計(jì)里,虛擬節(jié)點(diǎn)與虛擬節(jié)點(diǎn)之間,是一個(gè)TCP連接,是一個(gè)pipeline,數(shù)據(jù)塊一個(gè)接一個(gè)按順序在這個(gè)pipeline里等待處理。一旦某個(gè)數(shù)據(jù)塊的處理失敗,這個(gè)連接會(huì)被重置,后續(xù)的數(shù)據(jù)塊的處理都會(huì)失敗。因此不會(huì)存在Pipeline里一個(gè)數(shù)據(jù)塊更新失敗,但下一個(gè)數(shù)據(jù)塊成功的可能。

Split Brain的問題

選舉流程中,有個(gè)強(qiáng)制要求,那就是一定有超過半數(shù)的虛擬節(jié)點(diǎn)在線。但是如果replication正好是偶數(shù),這個(gè)時(shí)候,完全可能存在splt brain問題。

為解決這個(gè)問題,TDengine提供Arbitrator的解決方法。Arbitrator是一個(gè)節(jié)點(diǎn),它的任務(wù)就是接受任何虛擬節(jié)點(diǎn)的連接請求,并保持它。

在啟動(dòng)復(fù)制模塊實(shí)例時(shí),在配置參數(shù)中,應(yīng)用可以提供Arbitrator的IP地址。如果是奇數(shù)個(gè)副本,復(fù)制模塊不會(huì)與這個(gè)arbitrator去建立連接,但如果是偶數(shù)個(gè)副本,就會(huì)主動(dòng)去建立連接。

Arbitrator的程序tarbitrator.c在復(fù)制模塊的同一目錄, 編譯整個(gè)系統(tǒng)時(shí),會(huì)在bin目錄生成。命令行參數(shù)“-?”查看可以配置的參數(shù),比如綁定的IP地址,監(jiān)聽的端口號(hào)。

與RAFT相比的異同

數(shù)據(jù)一致性協(xié)議流行的有兩種,Paxos與Raft. 本設(shè)計(jì)的實(shí)現(xiàn)與Raft有很多類同之處,下面做一些比較

相同之處:

  • 三大流程一致:Raft里有Leader election, replication, safety,完全對應(yīng)TDengine的選舉、數(shù)據(jù)轉(zhuǎn)發(fā)、數(shù)據(jù)恢復(fù)三個(gè)流程。
  • 節(jié)點(diǎn)狀態(tài)定義一致:Raft里每個(gè)節(jié)點(diǎn)有Leader, Follower, Candidate三個(gè)狀態(tài),TDengine里是Master, Slave, Unsynced, Offline。多了一個(gè)offlince, 但本質(zhì)上是一樣的,因?yàn)閛ffline是外界看一個(gè)節(jié)點(diǎn)的狀態(tài),但該節(jié)點(diǎn)本身是處于master, slave 或unsynced的。
  • 數(shù)據(jù)轉(zhuǎn)發(fā)流程完全一樣,Master(leader)需要等待回復(fù)確認(rèn)。
  • 數(shù)據(jù)恢復(fù)流程幾乎一樣,Raft沒有涉及歷史數(shù)據(jù)同步問題,只考慮了WAL數(shù)據(jù)同步。

不同之處:

  • 選舉流程不一樣:Raft里任何一個(gè)節(jié)點(diǎn)是candidate時(shí),主動(dòng)向其他節(jié)點(diǎn)發(fā)出vote request,如果超過半數(shù)回答Yes,這個(gè)candidate就成為Leader,開始一個(gè)新的term。而TDengine的實(shí)現(xiàn)里,節(jié)點(diǎn)上線、離線或角色改變都會(huì)觸發(fā)狀態(tài)消息在節(jié)點(diǎn)組內(nèi)傳播,等節(jié)點(diǎn)組里狀態(tài)穩(wěn)定一致之后才觸發(fā)選舉流程,因?yàn)闋顟B(tài)穩(wěn)定一致,基于同樣的狀態(tài)信息,每個(gè)節(jié)點(diǎn)做出的決定會(huì)是一致的,一旦某個(gè)節(jié)點(diǎn)符合成為master的條件,無需其他節(jié)點(diǎn)認(rèn)可,它會(huì)自動(dòng)將自己設(shè)為master。TDengine里,任何一個(gè)節(jié)點(diǎn)檢測到其他節(jié)點(diǎn)或自己的角色發(fā)生改變,就會(huì)向節(jié)點(diǎn)組內(nèi)其他節(jié)點(diǎn)進(jìn)行廣播。Raft里不存在這樣的機(jī)制,因此需要投票來解決。
  • 對WAL的一條記錄,Raft用term + index來做唯一標(biāo)識(shí)。但TDengine只用version(類似index),在TDengine實(shí)現(xiàn)里,僅僅用version是完全可行的, 因?yàn)門Dengine的選舉機(jī)制,沒有term的概念。

如果整個(gè)虛擬節(jié)點(diǎn)組全部宕機(jī),重啟,但不是所有虛擬節(jié)點(diǎn)都上線,這個(gè)時(shí)候TDengine是不會(huì)選出master的,因?yàn)槲瓷暇€的節(jié)點(diǎn)有可能有最高version的數(shù)據(jù)。而RAFT協(xié)議,只要超過半數(shù)上線,就會(huì)選出Leader。

Meta Data的數(shù)據(jù)復(fù)制

TDengine里存在時(shí)序數(shù)據(jù),也存在Meta Data。Meta Data對數(shù)據(jù)的可靠性要求更高,那么TDengine設(shè)計(jì)能否滿足要求呢?下面做個(gè)仔細(xì)分析。

TDengine里Meta Data包括以下:

  • account 信息
  • 一個(gè)account下面,可以有多個(gè)user, 多個(gè)DB
  • 一個(gè)DB下面有多個(gè)vgroup
  • 一個(gè)DB下面有多個(gè)stable
  • 一個(gè)vgroup下面有多個(gè)table
  • 整個(gè)系統(tǒng)有多個(gè)mnode, dnode
  • 一個(gè)dnode可以有多個(gè)vnode

上述的account, user, DB, vgroup, table, stable, mnode, dnode都有自己的屬性,這些屬性是TDengine自己定義的,不會(huì)開放給用戶進(jìn)行修改。這些Meta Data的查詢都比較簡單,都可以采用key-value模型進(jìn)行存儲(chǔ)。這些Meta Data還具有幾個(gè)特點(diǎn):

  1. 上述的Meta Data之間有一定的層級(jí)關(guān)系,比如必須先創(chuàng)建DB,才能創(chuàng)建table, stable。只有先創(chuàng)建dnode,才可能創(chuàng)建vnode, 才可能創(chuàng)建vgroup。因此他們創(chuàng)建的順序是絕對不能錯(cuò)的。
  2. 在客戶端應(yīng)用的數(shù)據(jù)更新操作得到TDengine服務(wù)器側(cè)確認(rèn)后,所執(zhí)行的數(shù)據(jù)更新操作絕對不能丟失。否則會(huì)造成客戶端應(yīng)用與服務(wù)器的數(shù)據(jù)不一致。
  3. 上述的Meta Data是容許重復(fù)操作的。比如插入新記錄后,再插入一次,刪除一次后,再刪除一次,更新一次后,再更新一次,不會(huì)對系統(tǒng)產(chǎn)生任何影響,不會(huì)改變系統(tǒng)任何狀態(tài)。

對于特點(diǎn)1,本設(shè)計(jì)里,數(shù)據(jù)的寫入是單線程的,按照到達(dá)的先后順序,給每個(gè)數(shù)據(jù)更新操作打上版本號(hào),版本號(hào)大的記錄一定是晚于版本號(hào)小的寫入系統(tǒng),數(shù)據(jù)寫入順序是100%保證的,絕對不會(huì)讓版本號(hào)大的記錄先寫入。復(fù)制過程中,數(shù)據(jù)塊的轉(zhuǎn)發(fā)也是嚴(yán)格按照順序進(jìn)行的,因此TDengine的數(shù)據(jù)復(fù)制設(shè)計(jì)是能保證Meta Data的創(chuàng)建順序的。

對于特點(diǎn)2,只要Quorum數(shù)設(shè)置等于replica,那么一定能保證回復(fù)確認(rèn)過的數(shù)據(jù)更新操作不會(huì)在服務(wù)器側(cè)丟失。即使某節(jié)點(diǎn)永不起來,只要超過一半的節(jié)點(diǎn)還是online, 查詢服務(wù)不會(huì)受到任何影響。這時(shí),如果某個(gè)節(jié)點(diǎn)離線超過一定時(shí)長,系統(tǒng)可以自動(dòng)補(bǔ)充新的節(jié)點(diǎn),以保證在線的節(jié)點(diǎn)數(shù)在絕大部分時(shí)間是100%的。

對于特點(diǎn)3,完全可能發(fā)生,服務(wù)器確實(shí)持久化存儲(chǔ)了某一數(shù)據(jù)更新操作,但客戶端應(yīng)用出了問題,認(rèn)為操作不成功,它會(huì)重新發(fā)起操作。但對于Meta Data而言,沒有關(guān)系,客戶端可以再次發(fā)起同樣的操作,不會(huì)有任何影響。

總結(jié)來看,只要quorum設(shè)置大于一,本數(shù)據(jù)復(fù)制的設(shè)計(jì)是能滿足Meta Data的需求的。目前,還沒有發(fā)現(xiàn)漏洞。