TDengine 2.0 執行代碼taosd的設計
邏輯上,TDengine系統包含dnode, taosc和App,dnode是服務器側執行代碼taosd的一個運行實例,因此taosd是TDengine的核心,本文對taosd的設計做一簡單的介紹,模塊內的實現細節請見其他文檔。
系統模塊圖
taosd包含rpc, dnode, vnode, tsdb, query, cq, sync, wal, mnode, http, monitor等模塊,具體如下圖:

taosd的啟動入口是dnode模塊,dnode然后啟動其他模塊,包括可選配置的http, monitor模塊。taosc或dnode之間交互的消息都是通過rpc模塊進行,dnode模塊根據接收到的消息類型,將消息分發到vnode或mnode的消息隊列,或由dnode模塊自己消費。dnode的工作線程(worker)消費消息隊列里的消息,交給mnode或vnode進行處理。下面對各個模塊做簡要說明。
RPC模塊
該模塊負責taosd與taosc, 以及其他數據節點之間的通訊。TDengine沒有采取標準的HTTP或gRPC等第三方工具,而是實現了自己的通訊模塊RPC。
考慮到物聯網場景下,數據寫入的包一般不大,因此除支持TCP連接之外,RPC還支持UDP連接。當數據包小于15K時,RPC將采用UDP方式進行連接,否則將采用TCP連接。對于查詢類的消息,RPC不管包的大小,總是采取TCP連接。對于UDP連接,RPC實現了自己的超時、重傳、順序檢查等機制,以保證數據可靠傳輸。
RPC模塊還提供數據壓縮功能,如果數據包的字節數超過系統配置參數compressMsgSize, RPC在傳輸中將自動壓縮數據,以節省帶寬。
為保證數據的安全和數據的integrity, RPC模塊采用MD5做數字簽名,對數據的真實性和完整性進行認證。
DNODE模塊
該模塊是整個taosd的入口,它具體負責如下任務:
- 系統的初始化,包括
- 從文件taos.cfg讀取系統配置參數,從文件dnodeCfg.json讀取數據節點的配置參數;
- 啟動RPC模塊,并建立起與taosc通訊的server連接,與其他數據節點通訊的server連接;
- 啟動并初始化dnode的內部管理, 該模塊將掃描該數據節點已有的vnode,并打開它們;
- 初始化可配置的模塊,如mnode, http, monitor等。
- 數據節點的管理,包括
- 定時的向mnode發送status消息,報告自己的狀態;
- 根據mnode的指示,創建、改變、刪除vnode;
- 根據mnode的指示,修改自己的配置參數;
- 消息的分發、消費,包括
- 為每一個vnode和mnode的創建并維護一個讀隊列、一個寫隊列;
- 將從taosc或其他數據節點來的消息,根據消息類型,將其直接分發到不同的消息隊列,或由自己的管理模塊直接消費;
- 維護一個讀的線程池,消費讀隊列的消息,交給vnode或mnode處理。為支持高并發,一個讀線程(Worker)可以消費多個隊列的消息,一個讀隊列可以由多個worker消費;
- 維護一個寫的線程池,消費寫隊列的消息,交給vnode或mnode處理。為保證寫操作的序列化,一個寫隊列只能由一個寫線程負責,但一個寫線程可以負責多個寫隊列。
taosd的消息消費由dnode通過讀寫線程池進行控制,是系統的中樞。該模塊內的結構體圖如下:

VNODE模塊
vnode是一獨立的數據存儲查詢邏輯單元,但因為一個vnode只能容許一個DB,因此vnode內部沒有account, DB, user等概念。為實現更好的模塊化、封裝以及未來的擴展,它有很多子模塊,包括負責存儲的TSDB,負責查詢的Query, 負責數據復制的sync,負責數據庫日志的的wal, 負責連續查詢的cq(continuous query), 負責事件觸發的流計算的event等模塊,這些子模塊只與vnode模塊發生關系,與其他模塊沒有任何調用關系。模塊圖如下:

vnode模塊向下,與dnodeVRead,dnodeVWrite發生互動,向上,與子模塊發生互動。它主要的功能有:
- 協調各個子模塊的互動。各個子模塊之間都不直接調用,都需要通過vnode模塊進行;
- 對于來自taosc或mnode的寫操作,vnode模塊將其分解為寫日志(wal), 轉發(sync), 本地存儲(tsdb)子模塊的操作;
- 對于查詢操作,分發到query模塊進行。
一個數據節點里有多個vnode, 因此vnode模塊是有多個運行實例的。每個運行實例是完全獨立的。
vnode與其子模塊是通過API直接調用,而不是通過消息隊列傳遞。而且各個子模塊只與vnode模塊有交互,不與dnode, rpc等模塊發生任何直接關聯。
MNODE模塊
mnode是整個系統的大腦,負責整個系統的資源調度,負責meta data的管理與存儲。
一個運行的系統里,只有一個mnode,但它有多個副本(由系統配置參數numOfMnodes控制)。這些副本分布在不同的dnode里,目的是保證系統的高可靠運行。副本之間的數據復制是采用同步而非異步的方式,以確保數據的一致性,確保數據不會丟失。這些副本會自動選舉一個Master,其他副本是slave。所有數據更新類的操作,都只能在master上進行,而查詢類的可以在slave節點上進行。代碼實現上,同步模塊與vnode共享,但mnode被分配一個特殊的vgroup ID: 1,而且quorum大于1。整個集群系統是由多個dnode組成的,運行的mnode的副本數不可能超過dnode的個數,但不會超過配置的副本數。如果某個mnode副本宕機一段時間,只要超過半數的mnode副本仍在運行,運行的mnode會自動根據整個系統的資源情況,在其他dnode里再啟動一個mnode, 以保證運行的副本數。
各個dnode通過信息交換,保存有mnode各個副本的End Point列表,并向其中的master節點定時(間隔由系統配置參數statusInterval控制)發送status消息,消息體里包含該dnode的CPU、內存、剩余存儲空間、vnode個數,以及各個vnode的狀態(存儲空間、原始數據大小、記錄條數、角色等)。這樣mnode就了解整個系統的資源情況,如果用戶創建新的表,就可以決定需要在哪個dnode創建;如果增加或刪除dnode, 或者監測到某dnode數據過熱、或離線太長,就可以決定需要挪動那些vnode,以實現負載均衡。
mnode里還負責account, user, DB, stable, table, vgroup, dnode的創建、刪除與更新。mnode不僅把這些entity的meta data保存在內存,還做持久化存儲。但為節省內存,各個表的標簽值不保存在mnode(保存在vnode),而且子表不維護自己的schema, 而是與stable共享。為減小mnode的查詢壓力,taosc會緩存table、stable的schema。對于查詢類的操作,各個slave mnode也可以提供,以減輕master壓力。
TSDB模塊
TSDB模塊是VNODE中的負責快速高并發地存儲和讀取屬于該VNODE的表的元數據及采集的時序數據的引擎。除此之外,TSDB還提供了表結構的修改、表標簽值的修改等功能。TSDB提供API供VNODE和Query等模塊調用。TSDB中存儲了兩類數據,1:元數據信息;2:時序數據
元數據信息
TSDB中存儲的元數據包含屬于其所在的VNODE中表的類型,schema的定義等。對于超級表和超級表下的子表而言,又包含了tag的schema定義以及子表的tag值等。對于元數據信息而言,TSDB就相當于一個全內存的KV型數據庫,屬于該VNODE的表對象全部在內存中,方便快速查詢表的信息。除此之外,TSDB還對其中的子表,按照tag的第一列取值做了全內存的索引,大大加快了對于標簽的過濾查詢。TSDB中的元數據的最新狀態在落盤時,會以追加(append-only)的形式,寫入到meta文件中。meta文件只進行追加操作,即便是元數據的刪除,也會以一條記錄的形式寫入到文件末尾。TSDB也提供了對于元數據的修改操作,如表schema的修改,tag schema的修改以及tag值的修改等。
時序數據
每個TSDB在創建時,都會事先分配一定量的內存緩沖區,且內存緩沖區的大小可配可修改。表采集的時序數據,在寫入TSDB時,首先以追加的方式寫入到分配的內存緩沖區中,同時建立基于時間戳的內存索引,方便快速查詢。當內存緩沖區的數據積累到一定的程度時(達到內存緩沖區總大小的1/3),則會觸發落盤操作,將緩沖區中的數據持久化到硬盤文件上。時序數據在內存緩沖區中是以行(row)的形式存儲的。
而時序數據在寫入到TSDB的數據文件時,是以列(column)的形式存儲的。TSDB中的數據文件包含多個數據文件組,每個數據文件組中又包含.head、.data和.last三個文件,如(v2f1801.head、v2f1801.data、v2f1801.last)數據文件組。TSDB中的數據文件組是按照時間跨度進行分片的,默認是10天一個文件組,且可通過配置文件及建庫選項進行配置。分片的數據文件組又按照編號遞增排列,方便快速定位某一時間段的時序數據,高效定位數據文件組。時序數據在TSDB的數據文件中是以塊的形式進行列式存儲的,每個塊中只包含一張表的數據,且數據在一個塊中是按照時間順序遞增排列的。在一個數據文件組中,.head文件負責存儲數據塊的索引及統計信息,如每個塊的位置,壓縮算法,時間戳范圍等。存儲在.head文件中一張表的索引信息是按照數據塊中存儲的數據的時間遞增排列的,方便進行折半查找等工作。.head和.last文件是存儲真實數據塊的文件,若數據塊中的數據累計到一定程度,則會寫入.data文件中,否則,會寫入.last文件中,等待下次落盤時合并數據寫入.data文件中,從而大大減少文件中塊的個數,避免數據的過度碎片化。
Query模塊
該模塊負責整體系統的查詢處理。客戶端調用該該模塊進行SQL語法解析,并將查詢或寫入請求發送到vnode,同時負責針對超級表的查詢進行二階段的聚合操作。在Vnode端,該模塊調用TSDB模塊讀取系統中存儲的數據進行查詢處理。Query模塊還定義了系統能夠支持的全部查詢函數,查詢函數的實現機制與查詢框架無耦合,可以在不修改查詢流程的情況下動態增加查詢函數。詳細的設計請參見《TDengine 2.0查詢模塊設計》。
SYNC模塊
該模塊實現數據的多副本復制,包括vnode與mnode的數據復制,支持異步和同步兩種復制方式,以滿足meta data與時序數據不同復制的需求。因為它為mnode與vnode共享,系統為mnode副本預留了一個特殊的vgroup ID:1。因此vnode group的ID是從2開始的。
每個vnode/mnode模塊實例會有一對應的sync模塊實例,他們是一一對應的。詳細設計請見TDengine 2.0 數據復制模塊設計
WAL模塊
該模塊負責將新插入的數據寫入write ahead log(WAL), 為vnode, mnode共享。以保證服務器crash或其他故障,能從WAL中恢復數據。
每個vnode/mnode模塊實例會有一對應的wal模塊實例,是完全一一對應的。WAL的落盤操作由兩個參數walLevel, fsync控制。看具體場景,如果要100%保證數據不會丟失,需要將walLevel配置為2,fsync設置為0,每條數據插入請求,都會實時落盤后,才會給應用確認
HTTP模塊
該模塊負責處理系統對外的RESTful接口,可以通過配置,由dnode啟動或停止。
該模塊將接收到的RESTful請求,做了各種合法性檢查后,將其變成標準的SQL語句,通過taosc的異步接口,將請求發往整個系統中的任一dnode。收到處理后的結果后,再翻譯成HTTP協議,返回給應用。
如果HTTP模塊啟動,就意味著啟動了一個taosc的實例。任一一個dnode都可以啟動該模塊,以實現對RESTful請求的分布式處理。
Monitor模塊
該模塊負責檢測一個dnode的運行狀態,可以通過配置,由dnode啟動或停止。原則上,每個dnode都應該啟動一個monitor實例。
Monitor采集TDengine里的關鍵操作,比如創建、刪除、更新賬號、表、庫等,而且周期性的收集CPU、內存、網絡等資源的使用情況(采集周期由系統配置參數monitorInterval控制)。獲得這些數據后,monitor模塊將采集的數據寫入系統的日志庫(DB名字由系統配置參數monitorDbName控制)。
Monitor模塊使用taosc來將采集的數據寫入系統,因此每個monitor實例,都有一個taosc運行實例。

