數據模型和整體架構
數據模型
物聯網典型場景
在典(dian)型(xing)的(de)(de)(de)物聯網、車聯網、運維監測場景中,往(wang)(wang)往(wang)(wang)有(you)(you)多種不(bu)同(tong)類(lei)型(xing)的(de)(de)(de)數據(ju)(ju)采(cai)(cai)(cai)集(ji)設備(bei),采(cai)(cai)(cai)集(ji)一(yi)個(ge)到多個(ge)不(bu)同(tong)的(de)(de)(de)物理量。而同(tong)一(yi)種采(cai)(cai)(cai)集(ji)設備(bei)類(lei)型(xing),往(wang)(wang)往(wang)(wang)又有(you)(you)多個(ge)具體的(de)(de)(de)采(cai)(cai)(cai)集(ji)設備(bei)分(fen)布在不(bu)同(tong)的(de)(de)(de)地點。大數據(ju)(ju)處理系(xi)統(tong)就(jiu)是要(yao)將各種采(cai)(cai)(cai)集(ji)的(de)(de)(de)數據(ju)(ju)匯總,然后(hou)進行計算和分(fen)析。對于同(tong)一(yi)類(lei)設備(bei),其(qi)采(cai)(cai)(cai)集(ji)的(de)(de)(de)數據(ju)(ju)都是很規則的(de)(de)(de)。以智能(neng)電(dian)(dian)表為例,假設每個(ge)智能(neng)電(dian)(dian)表采(cai)(cai)(cai)集(ji)電(dian)(dian)流、電(dian)(dian)壓、相(xiang)位(wei)三個(ge)量,其(qi)采(cai)(cai)(cai)集(ji)的(de)(de)(de)數據(ju)(ju)類(lei)似(si)如下的(de)(de)(de)表格:
| 設備ID | 時間戳 | 采集量 | 標簽 | |||
|---|---|---|---|---|---|---|
| Device ID | Time Stamp | current | voltage | phase | location | groupId |
| d1001 | 1538548685000 | 10.3 | 219 | 0.31 | Beijing.Chaoyang | 2 |
| d1002 | 1538548684000 | 10.2 | 220 | 0.23 | Beijing.Chaoyang | 3 |
| d1003 | 1538548686500 | 11.5 | 221 | 0.35 | Beijing.Haidian | 3 |
| d1004 | 1538548685500 | 13.4 | 223 | 0.29 | Beijing.Haidian | 2 |
| d1001 | 1538548695000 | 12.6 | 218 | 0.33 | Beijing.Chaoyang | 2 |
| d1004 | 1538548696600 | 11.8 | 221 | 0.28 | Beijing.Haidian | 2 |
| d1002 | 1538548696650 | 10.3 | 218 | 0.25 | Beijing.Chaoyang | 3 |
| d1001 | 1538548696800 | 12.3 | 221 | 0.31 | Beijing.Chaoyang | 2 |
每一(yi)條記錄都有設備(bei) ID,時(shi)間戳,采(cai)集的(de)物理量(如(ru)上圖(tu)中的(de)電流、電壓、相位),還有與(yu)每個設備(bei)相關的(de)靜態標簽(如(ru)上述表1中的(de)位置 Location 和分組 groupId)。每個設備(bei)是受外界的(de)觸(chu)發,或按照設定的(de)周期采(cai)集數據(ju)(ju)。采(cai)集的(de)數據(ju)(ju)點是時(shi)序的(de),是一(yi)個數據(ju)(ju)流。
數據特征
除(chu)時序特征外(wai),仔細研究發現,物(wu)聯網(wang)、車聯網(wang)、運維(wei)監測類數據還(huan)具有很多其(qi)他明顯的(de)特征:
- 數據高度結構化;
- 數據極少有更新或刪除操作;
- 無需傳統數據庫的事務處理;
- 相對互聯網應用,寫多讀少;
- 流量平穩,根據設備數量和采集頻次,可以預測出來;
- 用戶關注的是一段時間的趨勢,而不是某一特定時間點的值;
- 數據有保留期限;
- 數據的查詢分析一定是基于時間段和空間區域;
- 除存儲、查詢操作外,還需要各種統計和實時計算操作;
- 數據量巨大,一天可能采集的數據就可以超過 100 億條。
充分利用(yong)上述特(te)征,TDengine 采取了(le)經特(te)殊優化的存儲(chu)和計算(suan)設計來處理時序數據,它將系(xi)統處理能(neng)力顯著提高,同時大幅降低了(le)系(xi)統運維的復(fu)雜度。
關系型數據庫模型
因為(wei)采集的數據(ju)(ju)一般(ban)是結構化(hua)數據(ju)(ju),同時為(wei)降低(di)學習(xi)門檻,TDengine 采用傳統的關系型數據(ju)(ju)庫模(mo)型管理數據(ju)(ju)。因此用戶(hu)需要先創(chuang)建庫,然后(hou)創(chuang)建表,之后(hou)才(cai)能插入或查詢數據(ju)(ju)。TDengine 采用的是結構化(hua)存儲,而不是 NoSQL 的 key-value 存儲。
一個數據采集點一張表
為充分利用其數據的時序性和其他數據特點,TDengine 要求對每個數據采集點單獨建表(比如有一(yi)千萬個智能電(dian)表(biao),就需(xu)創建(jian)一(yi)千萬張表(biao),上述表(biao)格中的 d1001, d1002, d1003, d1004 都需(xu)單獨(du)建(jian)表(biao)),用來存(cun)儲這個采集點(dian)所采集的時(shi)序數據。這種設(she)計有幾大優點(dian):
- 能保證一個采集點的數據在存儲介質上是以塊為單位連續存儲的。如果讀取一個時間段的數據,它能大幅減少隨機讀取操作,成數量級的提升讀取和查詢速度。
- 由于不同采集設備產生數據的過程完全獨立,每個設備的數據源是唯一的,一張表也就只有一個寫入者,這樣就可采用無鎖方式來寫,寫入速度就能大幅提升。
- 對于一個數據采集點而言,其產生的數據是時序的,因此寫的操作可用追加的方式實現,進一步大幅提高數據寫入速度。
如果采用傳統的方式,將多個設備的數據寫入一張表,由于網絡延時不可控,不同設備的數據到達服務器的時序是無法保證的,寫入操作是要有鎖保護的,而且一個設備的數據是難以保證連續存儲在一起的。采用一個數據采集點一張表的方式,能最大程度的保證單個數據采集點的插入和查詢的性能是最優的。
TDengine 建議(yi)用數(shu)(shu)據采(cai)集(ji)點的(de)名字(zi)(如(ru)上表(biao)(biao)中(zhong)的(de) D1001)來做(zuo)表(biao)(biao)名。每(mei)個(ge)數(shu)(shu)據采(cai)集(ji)點可能同(tong)時(shi)采(cai)集(ji)多(duo)個(ge)物理量(liang)(liang)(如(ru)上表(biao)(biao)中(zhong)的(de) current, voltage, phase),每(mei)個(ge)物理量(liang)(liang)對(dui)應一(yi)張表(biao)(biao)中(zhong)的(de)一(yi)列(lie),數(shu)(shu)據類型(xing)可以(yi)是整(zheng)型(xing)、浮點型(xing)、字(zi)符(fu)串等(deng)。除此(ci)之(zhi)外,表(biao)(biao)的(de)第一(yi)列(lie)必(bi)須是時(shi)間戳,即數(shu)(shu)據類型(xing)為 timestamp。對(dui)采(cai)集(ji)的(de)數(shu)(shu)據,TDengine 將自(zi)動按照(zhao)時(shi)間戳建立索(suo)引,但對(dui)采(cai)集(ji)的(de)物理量(liang)(liang)不(bu)建任(ren)何(he)索(suo)引。數(shu)(shu)據用列(lie)式存儲方式保(bao)存。
超級表:同一類型數據采集點的集合
由于一個數據采集(ji)點一張(zhang)表,導致表的(de)(de)數量(liang)巨增(zeng),難以管理,而且(qie)應用(yong)經常需(xu)要做采集(ji)點之間(jian)的(de)(de)聚合操(cao)作(zuo),聚合的(de)(de)操(cao)作(zuo)也變得復雜起來。為解決(jue)這個問題,TDengine 引入超級(ji)表(Super Table,簡(jian)稱為 STable)的(de)(de)概念。
超(chao)級表(biao)是(shi)指(zhi)某一(yi)特(te)定(ding)類(lei)(lei)型(xing)的(de)(de)數(shu)(shu)據(ju)采(cai)(cai)(cai)集(ji)(ji)點(dian)(dian)的(de)(de)集(ji)(ji)合(he)(he)。同一(yi)類(lei)(lei)型(xing)的(de)(de)數(shu)(shu)據(ju)采(cai)(cai)(cai)集(ji)(ji)點(dian)(dian),其表(biao)的(de)(de)結(jie)構是(shi)完全一(yi)樣(yang)的(de)(de),但每個(ge)表(biao)(數(shu)(shu)據(ju)采(cai)(cai)(cai)集(ji)(ji)點(dian)(dian))的(de)(de)靜態屬性(標(biao)(biao)簽(qian)(qian))是(shi)不(bu)一(yi)樣(yang)的(de)(de)。描述一(yi)個(ge)超(chao)級表(biao)(某一(yi)特(te)定(ding)類(lei)(lei)型(xing)的(de)(de)數(shu)(shu)據(ju)采(cai)(cai)(cai)集(ji)(ji)點(dian)(dian)的(de)(de)集(ji)(ji)合(he)(he)),除需要(yao)定(ding)義采(cai)(cai)(cai)集(ji)(ji)量的(de)(de)表(biao)結(jie)構之(zhi)外,還需要(yao)定(ding)義其標(biao)(biao)簽(qian)(qian)的(de)(de) schema,標(biao)(biao)簽(qian)(qian)的(de)(de)數(shu)(shu)據(ju)類(lei)(lei)型(xing)可以(yi)(yi)是(shi)整數(shu)(shu)、浮(fu)點(dian)(dian)數(shu)(shu)、字符(fu)串,標(biao)(biao)簽(qian)(qian)可以(yi)(yi)有多個(ge),可以(yi)(yi)事后增加(jia)、刪除或修改。如果整個(ge)系統有 N 個(ge)不(bu)同類(lei)(lei)型(xing)的(de)(de)數(shu)(shu)據(ju)采(cai)(cai)(cai)集(ji)(ji)點(dian)(dian),就需要(yao)建立(li) N 個(ge)超(chao)級表(biao)。
在 TDengine 的設計里,表用來代表一個具體的數據采集點,超級表用來代表一組相同類型的數據采集點集合。當為某個具體數據采集點創建表時,用戶使用超級表的定義做模板,同時指定該具體采集點(表)的標簽值。與傳統的關系型數據庫相比,表(一個數據采集點)是帶有靜態標簽的,而且這些標簽可以事后增加、刪除、修改。一張超級表包含有多張表,這些表具有相同的時序數據 schema,但帶有不同的標簽值。
當對(dui)多個具有相(xiang)同數據(ju)(ju)類型(xing)的(de)數據(ju)(ju)采集(ji)點進(jin)(jin)行(xing)聚(ju)合(he)操作(zuo)時,TDengine 會先把(ba)滿足標(biao)簽過濾條件的(de)表(biao)從超級表(biao)中找(zhao)出來,然后再掃描這些表(biao)的(de)時序(xu)數據(ju)(ju),進(jin)(jin)行(xing)聚(ju)合(he)操作(zuo),這樣需要(yao)掃描的(de)數據(ju)(ju)集(ji)會大幅減少,從而顯著提高聚(ju)合(he)計算的(de)性能。
集群與基本邏輯單元
TDengine 的(de)設計是基于(yu)單(dan)個硬件(jian)(jian)、軟件(jian)(jian)系(xi)統不(bu)可靠,基于(yu)任(ren)何單(dan)臺(tai)計算(suan)機都無法提供足夠計算(suan)能力和(he)存儲(chu)能力處理海(hai)量數據的(de)假設進(jin)行(xing)設計的(de)。因此 TDengine 從研發(fa)的(de)第(di)一(yi)天起,就按(an)照分布式高可靠架構進(jin)行(xing)設計,是支(zhi)持水平擴展的(de),這樣任(ren)何單(dan)臺(tai)或(huo)多臺(tai)服務器發(fa)生硬件(jian)(jian)故障或(huo)軟件(jian)(jian)錯誤都不(bu)影響系(xi)統的(de)可用(yong)性和(he)可靠性。同時(shi),通過節點虛擬化(hua)并輔(fu)以自動化(hua)負載均衡技術,TDengine 能最高效率地利用(yong)異構集群中的(de)計算(suan)和(he)存儲(chu)資源降低硬件(jian)(jian)投資。
主要邏輯單元
TDengine 分布式架構的邏輯結構圖(tu)如下(xia):

一(yi)個(ge)(ge)完整的 TDengine 系(xi)統(tong)是運行(xing)在一(yi)到多(duo)個(ge)(ge)物理節點(dian)上(shang)的,邏輯上(shang),它包含數(shu)據(ju)節點(dian)(dnode)、TDengine 應用驅動(dong)(taosc)以(yi)及應用(app)。系(xi)統(tong)中存在一(yi)到多(duo)個(ge)(ge)數(shu)據(ju)節點(dian),這些數(shu)據(ju)節點(dian)組成一(yi)個(ge)(ge)集(ji)群(cluster)。應用通過 taosc 的 API 與 TDengine 集(ji)群進(jin)行(xing)互動(dong)。下面對每個(ge)(ge)邏輯單元進(jin)行(xing)簡要介紹。
物理節點(pnode): pnode 是一獨立運行、擁有自己的計算、存儲和網絡能力的計算機,可以是安裝有OS的物理機、虛擬機或 Docker 容器。物理節點由其配置的 FQDN (Fully Qualified Domain Name)來標識。TDengine 完全依賴 FQDN 來進行網絡通訊,如果不了解 FQDN,請看博文《一篇文章說清楚 TDengine 的 FQDN》。
數據節點(dnode): dnode 是(shi) TDengine 服務器(qi)側執行(xing)代碼(ma) taosd 在物理(li)節點(dian)(dian)(dian)(dian)上的(de)(de)一(yi)(yi)個(ge)運(yun)行(xing)實(shi)例,一(yi)(yi)個(ge)工(gong)作的(de)(de)系統(tong)(tong)必須有至少一(yi)(yi)個(ge)數據節點(dian)(dian)(dian)(dian)。dnode 包含零(ling)到多個(ge)邏(luo)輯的(de)(de)虛擬節點(dian)(dian)(dian)(dian)(vnode),零(ling)或者(zhe)至多一(yi)(yi)個(ge)邏(luo)輯的(de)(de)管(guan)理(li)節點(dian)(dian)(dian)(dian)(mnode)。dnode 在系統(tong)(tong)中的(de)(de)唯一(yi)(yi)標識由實(shi)例的(de)(de) End Point (EP)決定。EP 是(shi) dnode 所在物理(li)節點(dian)(dian)(dian)(dian)的(de)(de) FQDN (Fully Qualified Domain Name)和(he)系統(tong)(tong)所配置的(de)(de)網絡端口號(Port)的(de)(de)組合。通過配置不同的(de)(de)端口,一(yi)(yi)個(ge)物理(li)節點(dian)(dian)(dian)(dian)(一(yi)(yi)臺物理(li)機、虛擬機或容器(qi))可以(yi)運(yun)行(xing)多個(ge)實(shi)例,或有多個(ge)數據節點(dian)(dian)(dian)(dian)。
虛擬節點(vnode): 為更好的(de)(de)(de)(de)支持數(shu)據(ju)分片、負載均(jun)衡,防止(zhi)數(shu)據(ju)過熱(re)或傾斜(xie),數(shu)據(ju)節(jie)點被虛擬化成多個(ge)虛擬節(jie)點(vnode,圖中 V2, V3, V4等)。每個(ge) vnode 都是一(yi)(yi)(yi)(yi)(yi)個(ge)相對(dui)獨(du)立的(de)(de)(de)(de)工作(zuo)單元,是時序數(shu)據(ju)存(cun)儲的(de)(de)(de)(de)基本單元,具有獨(du)立的(de)(de)(de)(de)運行線程、內存(cun)空間與持久化存(cun)儲的(de)(de)(de)(de)路徑。一(yi)(yi)(yi)(yi)(yi)個(ge) vnode 包(bao)含一(yi)(yi)(yi)(yi)(yi)定數(shu)量的(de)(de)(de)(de)表(數(shu)據(ju)采(cai)集點)。當創(chuang)建一(yi)(yi)(yi)(yi)(yi)張新表時,系統(tong)會檢查是否需要創(chuang)建新的(de)(de)(de)(de) vnode。一(yi)(yi)(yi)(yi)(yi)個(ge)數(shu)據(ju)節(jie)點上能創(chuang)建的(de)(de)(de)(de) vnode 的(de)(de)(de)(de)數(shu)量取決(jue)于(yu)該數(shu)據(ju)節(jie)點所(suo)在物理(li)節(jie)點的(de)(de)(de)(de)硬件資源。一(yi)(yi)(yi)(yi)(yi)個(ge) vnode 只(zhi)屬于(yu)一(yi)(yi)(yi)(yi)(yi)個(ge) DB,但一(yi)(yi)(yi)(yi)(yi)個(ge) DB 可以有多個(ge) vnode。一(yi)(yi)(yi)(yi)(yi)個(ge) vnode 除存(cun)儲的(de)(de)(de)(de)時序數(shu)據(ju)外,也(ye)保(bao)存(cun)有所(suo)包(bao)含的(de)(de)(de)(de)表的(de)(de)(de)(de) schema、標簽值(zhi)等。一(yi)(yi)(yi)(yi)(yi)個(ge)虛擬節(jie)點由所(suo)屬的(de)(de)(de)(de)數(shu)據(ju)節(jie)點的(de)(de)(de)(de)EP,以及所(suo)屬的(de)(de)(de)(de) VGroup ID 在系統(tong)內唯一(yi)(yi)(yi)(yi)(yi)標識(shi),由管理(li)節(jie)點創(chuang)建并管理(li)。
管理節點(mnode): 一(yi)個虛(xu)擬的(de)(de)邏輯單元,負責所(suo)有數(shu)(shu)據節(jie)點運行(xing)(xing)狀態的(de)(de)監控和維護(hu),以及節(jie)點之間(jian)的(de)(de)負載(zai)均衡(圖(tu)中 M)。同時,管理節(jie)點也負責元數(shu)(shu)據(包括用(yong)(yong)戶、數(shu)(shu)據庫、表、靜態標簽等(deng))的(de)(de)存儲和管理,因此也稱為 Meta Node。TDengine 集(ji)(ji)群中可配(pei)置多(duo)(duo)個(最多(duo)(duo)不超過 3 個) mnode,它們自(zi)動構(gou)建(jian)成為一(yi)個虛(xu)擬管理節(jie)點組(圖(tu)中 M0, M1, M2)。mnode 間(jian)采(cai)用(yong)(yong) master/slave 的(de)(de)機制(zhi)進(jin)行(xing)(xing)管理,而且(qie)采(cai)取(qu)強一(yi)致方式進(jin)行(xing)(xing)數(shu)(shu)據同步, 任何數(shu)(shu)據更新(xin)操作只能在 Master 上(shang)進(jin)行(xing)(xing)。mnode 集(ji)(ji)群的(de)(de)創建(jian)由(you)系統(tong)自(zi)動完成,無需(xu)人工干預。每個 dnode 上(shang)至(zhi)多(duo)(duo)有一(yi)個 mnode,由(you)所(suo)屬的(de)(de)數(shu)(shu)據節(jie)點的(de)(de)EP來唯一(yi)標識。每個 dnode 通過內部消息交互(hu)自(zi)動獲(huo)取(qu)整(zheng)個集(ji)(ji)群中所(suo)有 mnode 所(suo)在的(de)(de) dnode 的(de)(de)EP。
虛擬節點組(VGroup): 不同(tong)數(shu)據(ju)節(jie)點上的(de)(de)(de) vnode 可(ke)以組(zu)成一個(ge)(ge)(ge)(ge)虛(xu)(xu)擬節(jie)點組(zu)(vnode group)來(lai)保證系(xi)(xi)統(tong)的(de)(de)(de)高可(ke)靠。虛(xu)(xu)擬節(jie)點組(zu)內采取 master/slave 的(de)(de)(de)方式進(jin)行管(guan)理(li)(li)。寫操作只(zhi)能在(zai) master vnode 上進(jin)行,系(xi)(xi)統(tong)采用異(yi)步復制(zhi)的(de)(de)(de)方式將數(shu)據(ju)同(tong)步到 slave vnode,這樣確保了一份(fen)數(shu)據(ju)在(zai)多個(ge)(ge)(ge)(ge)物理(li)(li)節(jie)點上有拷貝。一個(ge)(ge)(ge)(ge) vgroup 里(li)虛(xu)(xu)擬節(jie)點個(ge)(ge)(ge)(ge)數(shu)就(jiu)(jiu)是數(shu)據(ju)的(de)(de)(de)副(fu)本(ben)數(shu)。如(ru)果一個(ge)(ge)(ge)(ge) DB 的(de)(de)(de)副(fu)本(ben)數(shu)為 N,系(xi)(xi)統(tong)必須有至少 N 數(shu)據(ju)節(jie)點。副(fu)本(ben)數(shu)在(zai)創建DB時(shi)通過參數(shu) replica 可(ke)以指定,缺(que)省為 1。使用 TDengine 的(de)(de)(de)多副(fu)本(ben)特性,可(ke)以不再需要(yao)昂貴的(de)(de)(de)磁(ci)盤陣(zhen)列等存儲(chu)設備,就(jiu)(jiu)可(ke)以獲得同(tong)樣的(de)(de)(de)數(shu)據(ju)高可(ke)靠性。虛(xu)(xu)擬節(jie)點組(zu)由(you)管(guan)理(li)(li)節(jie)點創建、管(guan)理(li)(li),并且由(you)管(guan)理(li)(li)節(jie)點分配一個(ge)(ge)(ge)(ge)系(xi)(xi)統(tong)唯(wei)一的(de)(de)(de) ID,VGroup ID。如(ru)果兩個(ge)(ge)(ge)(ge)虛(xu)(xu)擬節(jie)點的(de)(de)(de) vnode group ID 相同(tong),說明他們(men)屬于同(tong)一個(ge)(ge)(ge)(ge)組(zu),數(shu)據(ju)互為備份(fen)。虛(xu)(xu)擬節(jie)點組(zu)里(li)虛(xu)(xu)擬節(jie)點的(de)(de)(de)個(ge)(ge)(ge)(ge)數(shu)是可(ke)以動態改變的(de)(de)(de),容許只(zhi)有一個(ge)(ge)(ge)(ge),也就(jiu)(jiu)是沒(mei)有數(shu)據(ju)復制(zhi)。VGroup ID 是永遠不變的(de)(de)(de),即(ji)使一個(ge)(ge)(ge)(ge)虛(xu)(xu)擬節(jie)點組(zu)被刪除,它的(de)(de)(de)ID也不會被收回重復利(li)用。
TAOSC: taosc 是 TDengine 給應(ying)(ying)用(yong)(yong)(yong)提(ti)供的(de)驅動程序(driver),負(fu)責處理應(ying)(ying)用(yong)(yong)(yong)與(yu)集群的(de)接(jie)(jie)口交互(hu),提(ti)供 C/C++ 語言原生接(jie)(jie)口,內嵌于(yu) JDBC、C#、Python、Go、Node.js 語言連接(jie)(jie)庫(ku)里。應(ying)(ying)用(yong)(yong)(yong)都(dou)是通過 taosc 而不是直接(jie)(jie)連接(jie)(jie)集群中(zhong)的(de)數(shu)據節點(dian)與(yu)整個(ge)集群進(jin)行(xing)(xing)交互(hu)的(de)。這個(ge)模塊負(fu)責獲(huo)取并緩存(cun)元(yuan)數(shu)據;將(jiang)插入、查詢(xun)等(deng)請(qing)求轉發到正確的(de)數(shu)據節點(dian);在(zai)把(ba)結果返(fan)回(hui)給應(ying)(ying)用(yong)(yong)(yong)時,還需要負(fu)責最后一級的(de)聚合、排序、過濾等(deng)操作。對于(yu) JDBC、C/C++、C#、Python、Go、Node.js 接(jie)(jie)口而言,這個(ge)模塊是在(zai)應(ying)(ying)用(yong)(yong)(yong)所處的(de)物理節點(dian)上運行(xing)(xing)。同時,為支持全分布式的(de) RESTful 接(jie)(jie)口,taosc 在(zai) TDengine 集群的(de)每(mei)個(ge) dnode 上都(dou)有一運行(xing)(xing)實例。
節點之間的通訊
通訊方式:TDengine 系統的(de)(de)各個數(shu)(shu)據(ju)節點(dian)之(zhi)(zhi)間,以及(ji)應(ying)用(yong)驅動與各數(shu)(shu)據(ju)節點(dian)之(zhi)(zhi)間的(de)(de)通訊是通過 TCP/UDP 進行(xing)的(de)(de)。因(yin)(yin)為考慮到物聯網場景,數(shu)(shu)據(ju)寫入(ru)的(de)(de)包一般不大,因(yin)(yin)此 TDengine 除(chu)采(cai)用(yong) TCP 做傳輸(shu)(shu)之(zhi)(zhi)外,還采(cai)用(yong) UDP 方(fang)式(shi),因(yin)(yin)為 UDP 更加高(gao)效,而且不受連接數(shu)(shu)的(de)(de)限(xian)制(zhi)。TDengine 實現了自己的(de)(de)超(chao)時、重傳、確(que)認等機制(zhi),以確(que)保 UDP 的(de)(de)可靠傳輸(shu)(shu)。對于(yu)數(shu)(shu)據(ju)量不到15K的(de)(de)數(shu)(shu)據(ju)包,采(cai)取(qu) UDP 的(de)(de)方(fang)式(shi)進行(xing)傳輸(shu)(shu),超(chao)過 15K 的(de)(de),或者是查詢類的(de)(de)操作,自動采(cai)取(qu) TCP 的(de)(de)方(fang)式(shi)進行(xing)傳輸(shu)(shu)。同時,TDengine 根據(ju)配置和數(shu)(shu)據(ju)包,會自動對數(shu)(shu)據(ju)進行(xing)壓縮/解壓縮,數(shu)(shu)字簽名/認證等處理。對于(yu)數(shu)(shu)據(ju)節點(dian)之(zhi)(zhi)間的(de)(de)數(shu)(shu)據(ju)復制(zhi),只采(cai)用(yong) TCP 方(fang)式(shi)進行(xing)數(shu)(shu)據(ju)傳輸(shu)(shu)。
FQDN配置:一個數(shu)(shu)(shu)據(ju)節(jie)(jie)點(dian)有(you)一個或(huo)多個 FQDN,可(ke)以在系統配(pei)置文(wen)件 taos.cfg 通(tong)過(guo)參數(shu)(shu)(shu)"fqdn"進(jin)行(xing)指定(ding),如(ru)果沒(mei)有(you)指定(ding),系統將(jiang)自(zi)動獲(huo)取計算機的(de) hostname 作(zuo)為(wei)其 FQDN。如(ru)果節(jie)(jie)點(dian)沒(mei)有(you)配(pei)置 FQDN,可(ke)以直(zhi)接(jie)將(jiang)該節(jie)(jie)點(dian)的(de)配(pei)置參數(shu)(shu)(shu) fqdn 設(she)置為(wei)它的(de)IP地(di)址。但不建議使用 IP,因為(wei) IP 地(di)址可(ke)變,一旦變化,將(jiang)讓(rang)集群(qun)無法正常(chang)(chang)工(gong)作(zuo)。一個數(shu)(shu)(shu)據(ju)節(jie)(jie)點(dian)的(de) EP(End Point) 由 FQDN + Port 組成(cheng)。采用 FQDN,需要(yao)保證 DNS 服(fu)務正常(chang)(chang)工(gong)作(zuo),或(huo)者在節(jie)(jie)點(dian)以及應用所在的(de)節(jie)(jie)點(dian)配(pei)置好 hosts 文(wen)件。另外(wai),這(zhe)個參數(shu)(shu)(shu)值的(de)長(chang)度需要(yao)控(kong)制在 96 個字符以內。
端口配置:一(yi)個(ge)(ge)數據節(jie)點對(dui)外的(de)(de)端口由 TDengine 的(de)(de)系統(tong)配置(zhi)參數 serverPort 決定,對(dui)集群內部通訊的(de)(de)端口是 serverPort+5。為支持多線(xian)程高效的(de)(de)處理 UDP 數據,每個(ge)(ge)對(dui)內和(he)對(dui)外的(de)(de) UDP 連接(jie),都需要占用5個(ge)(ge)連續的(de)(de)端口。
- 集群內數據節點之間的數據復制操作占用一個 TCP 端口,是 serverPort+10。
- 集群數據節點對外提供 RESTful 服務占用一個 TCP 端口,是 serverPort+11。
- 集群內數據節點與 Arbitrator 節點之間通訊占用一個 TCP 端口,是 serverPort+12。
因此一個數據節點總的端口范圍為 serverPort 到 serverPort+12,總共 13 個 TCP/UDP 端口。使用時,需要確保防火墻將這些端口打開。每個數據節點可以配置不同的 serverPort。(詳細的端口情況請參見 TDengine 2.0 端口說明)
集群對外連接:TDengine 集(ji)群可(ke)以容納單個、多(duo)個甚至幾千個數(shu)(shu)(shu)據節點(dian)。應用只需(xu)要向集(ji)群中任何一(yi)個數(shu)(shu)(shu)據節點(dian)發起連接即可(ke),連接需(xu)要提(ti)供的(de)網(wang)絡(luo)參數(shu)(shu)(shu)是一(yi)數(shu)(shu)(shu)據節點(dian)的(de) End Point(FQDN加配(pei)置(zhi)的(de)端(duan)口號(hao))。通過命令行(xing)CLI啟動應用 taos 時,可(ke)以通過選項-h來指定數(shu)(shu)(shu)據節點(dian)的(de) FQDN, -P 來指定其配(pei)置(zhi)的(de)端(duan)口號(hao),如果端(duan)口不(bu)配(pei)置(zhi),將采用 TDengine 的(de)系統配(pei)置(zhi)參數(shu)(shu)(shu) serverPort。
集群內部通訊:各個(ge)數據節點之(zhi)間通過 TCP/UDP 進(jin)行連接(jie)。一個(ge)數據節點啟動時,將獲(huo)取 mnode 所在的 dnode 的 EP 信息,然后與(yu)系統中的 mnode 建立起連接(jie),交換信息。獲(huo)取 mnode 的 EP 信息有(you)三步:
- 檢查 mnodeEpSet.json 文件是否存在,如果不存在或不能正常打開獲得 mnode EP 信息,進入第二步;
- 檢查系統配置文件 taos.cfg,獲取節點配置參數 firstEp、secondEp(這兩個參數指定的節點可以是不帶 mnode 的普通節點,這樣的話,節點被連接時會嘗試重定向到 mnode 節點),如果不存在或者 taos.cfg 里沒有這兩個配置參數,或無效,進入第三步;
- 將自己的EP設為 mnode EP,并獨立運行起來。
獲取 mnode EP 列表后,數(shu)據(ju)節點發(fa)起連(lian)(lian)接(jie),如果(guo)連(lian)(lian)接(jie)成(cheng)功,則(ze)成(cheng)功加入進工(gong)作(zuo)的(de)集群(qun),如果(guo)不(bu)成(cheng)功,則(ze)嘗(chang)(chang)試(shi)(shi) mnode EP 列表中的(de)下一個(ge)。如果(guo)都嘗(chang)(chang)試(shi)(shi)了,但連(lian)(lian)接(jie)都仍然失敗,則(ze)休(xiu)眠幾(ji)秒后,再(zai)進行嘗(chang)(chang)試(shi)(shi)。
MNODE的選擇:TDengine 邏輯上(shang)有(you)管(guan)理節(jie)(jie)點(dian)(dian),但沒有(you)單獨的(de)(de)(de)(de)執(zhi)行(xing)(xing)代碼,服務器側(ce)只(zhi)有(you)一套(tao)執(zhi)行(xing)(xing)代碼 taosd。那么哪(na)個(ge)(ge)數(shu)(shu)據(ju)(ju)節(jie)(jie)點(dian)(dian)會是(shi)管(guan)理節(jie)(jie)點(dian)(dian)呢?這是(shi)系統自(zi)(zi)(zi)(zi)動決(jue)定的(de)(de)(de)(de),無需任(ren)何人工(gong)干預。原則如(ru)下(xia):一個(ge)(ge)數(shu)(shu)據(ju)(ju)節(jie)(jie)點(dian)(dian)啟(qi)(qi)動時,會檢查自(zi)(zi)(zi)(zi)己的(de)(de)(de)(de) End Point, 并與獲(huo)取的(de)(de)(de)(de) mnode EP List 進行(xing)(xing)比(bi)對,如(ru)果在其中,該數(shu)(shu)據(ju)(ju)節(jie)(jie)點(dian)(dian)認為(wei)自(zi)(zi)(zi)(zi)己應該啟(qi)(qi)動 mnode 模塊(kuai),成為(wei) mnode。如(ru)果自(zi)(zi)(zi)(zi)己的(de)(de)(de)(de) EP 不在 mnode EP List 里,則不啟(qi)(qi)動 mnode 模塊(kuai)。在系統的(de)(de)(de)(de)運行(xing)(xing)過程中,由于負載均(jun)衡、宕機等原因,mnode 有(you)可能遷移(yi)至新的(de)(de)(de)(de) dnode,但一切都是(shi)透(tou)明的(de)(de)(de)(de),無需人工(gong)干預,配置(zhi)參(can)數(shu)(shu)的(de)(de)(de)(de)修改,是(shi) mnode 自(zi)(zi)(zi)(zi)己根據(ju)(ju)資源做(zuo)出的(de)(de)(de)(de)決(jue)定。
新數據節點的加入:系統(tong)有(you)了一(yi)(yi)個(ge)數(shu)(shu)(shu)據節點后,就(jiu)已經成為一(yi)(yi)個(ge)工(gong)作(zuo)的(de)系統(tong)。添加新的(de)節點進集群(qun)時(shi),有(you)兩個(ge)步(bu)驟,第一(yi)(yi)步(bu):使用(yong) TDengine CLI 連接到(dao)現有(you)工(gong)作(zuo)的(de)數(shu)(shu)(shu)據節點,然后用(yong)命(ming)令"create dnode"將新的(de)數(shu)(shu)(shu)據節點的(de) End Point 添加進去; 第二步(bu):在新的(de)數(shu)(shu)(shu)據節點的(de)系統(tong)配置(zhi)(zhi)參數(shu)(shu)(shu)文件 taos.cfg 里,將 firstEp, secondEp 參數(shu)(shu)(shu)設置(zhi)(zhi)為現有(you)集群(qun)中(zhong)任意兩個(ge)數(shu)(shu)(shu)據節點的(de) EP 即(ji)可。具體添加的(de)詳細步(bu)驟請見詳細的(de)用(yong)戶手冊。這(zhe)樣就(jiu)把集群(qun)一(yi)(yi)步(bu)一(yi)(yi)步(bu)的(de)建立起(qi)來。
重定向:無論是(shi)(shi) dnode 還是(shi)(shi) taosc,最先都是(shi)(shi)要發起與 mnode 的(de)連(lian)接,但 mnode 是(shi)(shi)系統自動創建并(bing)(bing)維護(hu)的(de),因此對于用(yong)戶來(lai)(lai)說,并(bing)(bing)不知道哪個(ge) dnode 在運(yun)行 mnode。TDengine 只要求向(xiang)系統中任(ren)何一(yi)個(ge)工作的(de) dnode 發起連(lian)接即可。因為(wei)任(ren)何一(yi)個(ge)正在運(yun)行的(de) dnode,都維護(hu)有(you)目(mu)前運(yun)行的(de) mnode EP List。當收到(dao)一(yi)個(ge)來(lai)(lai)自新啟動的(de) dnode 或(huo) taosc 的(de)連(lian)接請求,如果自己不是(shi)(shi) mnode,則將 mnode EP List 回復給對方,taosc 或(huo)新啟動的(de) dnode 收到(dao)這個(ge) list, 就(jiu)重新嘗(chang)試建立連(lian)接。當 mnode EP List 發生改變,通(tong)過節(jie)點(dian)之間的(de)消息交互,各個(ge)數據節(jie)點(dian)就(jiu)很快獲取最新列表,并(bing)(bing)通(tong)知 taosc。
一個典型的消息流程
為解釋 vnode、mnode、taosc 和應用之間的關系以及各(ge)自扮演(yan)的角色,下面對寫入數據(ju)這個典型(xing)操作的流程(cheng)進(jin)行(xing)剖(pou)析。

- 應用通過 JDBC 或其他API接口發起插入數據的請求。
- taosc 會檢查緩存,看是否保存有該表的 meta data。如果有,直接到第 4 步。如果沒有,taosc 將向 mnode 發出 get meta-data 請求。
- mnode 將該表的 meta-data 返回給 taosc。Meta-data 包含有該表的 schema, 而且還有該表所屬的 vgroup信息(vnode ID 以及所在的 dnode 的 End Point,如果副本數為 N,就有 N 組 End Point)。如果 taosc 遲遲得不到 mnode 回應,而且存在多個 mnode, taosc 將向下一個 mnode 發出請求。
- taosc 向 master vnode 發起插入請求。
- vnode 插入數據后,給 taosc 一個應答,表示插入成功。如果 taosc 遲遲得不到 vnode 的回應,taosc 會認為該節點已經離線。這種情況下,如果被插入的數據庫有多個副本,taosc 將向 vgroup 里下一個 vnode 發出插入請求。
- taosc 通知 APP,寫入成功。
對于第二和第三步,taosc 啟動(dong)時,并不(bu)知(zhi)道 mnode 的(de)(de) End Point,因(yin)此會(hui)(hui)直接(jie)(jie)向(xiang)配置的(de)(de)集群對外(wai)服務的(de)(de) End Point 發(fa)起請(qing)求(qiu)。如果(guo)接(jie)(jie)收到該(gai)請(qing)求(qiu)的(de)(de) dnode 并沒有配置 mnode,該(gai) dnode 會(hui)(hui)在回復的(de)(de)消息中告知(zhi)mnode EP 列表,這樣 taosc 會(hui)(hui)重新(xin)向(xiang)新(xin)的(de)(de) mnode 的(de)(de) EP 發(fa)出獲取 meta-data 的(de)(de)請(qing)求(qiu)。
對(dui)于第四和第五步,沒有(you)緩(huan)(huan)存(cun)的(de)(de)情況下,taosc 無(wu)法(fa)知道虛擬節(jie)點組里誰是(shi)(shi)(shi) master,就假(jia)設第一個 vnodeID 就是(shi)(shi)(shi) master,向它(ta)發(fa)出請(qing)求。如果接收(shou)到請(qing)求的(de)(de) vnode 并不是(shi)(shi)(shi) master,它(ta)會(hui)在回復(fu)中(zhong)告知誰是(shi)(shi)(shi) master,這(zhe)樣 taosc 就向建(jian)議(yi)的(de)(de) master vnode 發(fa)出請(qing)求。一旦得到插入成功(gong)的(de)(de)回復(fu),taosc 會(hui)緩(huan)(huan)存(cun) master 節(jie)點的(de)(de)信息。
上述(shu)是插入數(shu)據的(de)流(liu)程,查詢、計算的(de)流(liu)程也完全(quan)一(yi)致。taosc 把這些復雜的(de)流(liu)程全(quan)部封裝屏蔽了,對(dui)于應用(yong)來(lai)說無(wu)感知也無(wu)需任(ren)何(he)特別處(chu)理。
通過 taosc 緩存機制,只有(you)在第一次對一張表操作時(shi),才需要訪問(wen) mnode,因此 mnode 不會(hui)成(cheng)為系統瓶頸。但因為 schema 有(you)可(ke)能變(bian)化,而且 vgroup 有(you)可(ke)能發(fa)生改變(bian)(比如負載(zai)均衡(heng)發(fa)生),因此 taosc 會(hui)定時(shi)和mnode 交互(hu),自動更新緩存。
存儲模型與數據分區、分片
存儲模型
TDengine 存儲的(de)數據(ju)(ju)包括(kuo)采集(ji)的(de)時序數據(ju)(ju)以及(ji)庫(ku)、表相(xiang)關的(de)元數據(ju)(ju)、標簽數據(ju)(ju)等(deng),這些數據(ju)(ju)具體分(fen)(fen)為三部分(fen)(fen):
- 時序數據:存放于 vnode 里,由 data、head 和 last 三個文件組成,數據量大,查詢量取決于應用場景。容許亂序寫入,但暫時不支持刪除操作,并且僅在 update 參數設置為 1 時允許更新操作。通過采用一個采集點一張表的模型,一個時間段的數據是連續存儲,對單張表的寫入是簡單的追加操作,一次讀,可以讀到多條記錄,這樣保證對單個采集點的插入和查詢操作,性能達到最優。
- 標簽數據:存放于 vnode 里的 meta 文件,支持增刪改查四個標準操作。數據量不大,有 N 張表,就有 N 條記錄,因此可以全內存存儲。如果標簽過濾操作很多,查詢將十分頻繁,因此 TDengine 支持多核多線程并發查詢。只要計算資源足夠,即使有數千萬張表,過濾結果能毫秒級返回。
- 元數據:存放于 mnode 里,包含系統節點、用戶、DB、Table Schema 等信息,支持增刪改查四個標準操作。這部分數據的量不大,可以全內存保存,而且由于客戶端有緩存,查詢量也不大。因此目前的設計雖是集中式存儲管理,但不會構成性能瓶頸。
與典型的(de) NoSQL 存儲模型相(xiang)比,TDengine 將(jiang)標簽數(shu)據(ju)與時序(xu)數(shu)據(ju)完(wan)全分離存儲,它(ta)具有(you)兩(liang)大優勢(shi):
- 能夠極大地降低標簽數據存儲的冗余度:一般的 NoSQL 數據庫或時序數據庫,采用的 K-V 存儲,其中的 Key 包含時間戳、設備 ID、各種標簽。每條記錄都帶有這些重復的內容,浪費存儲空間。而且如果應用要在歷史數據上增加、修改或刪除標簽,需要遍歷數據,重寫一遍,操作成本極其昂貴。
- 能夠實現極為高效的多表之間的聚合查詢:做多表之間聚合查詢時,先把符合標簽過濾條件的表查找出來,然后再查找這些表相應的數據塊,這樣大幅減少要掃描的數據集,從而大幅提高查詢效率。而且標簽數據采用全內存的結構進行管理和維護,千萬級別規模的標簽數據查詢可以在毫秒級別返回。
數據分片
對于海量的(de)數(shu)(shu)據(ju)管(guan)理,為實(shi)現(xian)水平(ping)擴展,一般(ban)都需要采(cai)取分(fen)片(Sharding)分(fen)區(Partitioning)策略。TDengine 是通(tong)過(guo) vnode 來(lai)實(shi)現(xian)數(shu)(shu)據(ju)分(fen)片的(de),通(tong)過(guo)一個時間段一個數(shu)(shu)據(ju)文件來(lai)實(shi)現(xian)時序數(shu)(shu)據(ju)分(fen)區的(de)。
vnode(虛擬數(shu)(shu)據(ju)節點(dian)(dian))負責(ze)為采集的(de)時序數(shu)(shu)據(ju)提供寫(xie)入(ru)、查(cha)詢和(he)(he)計算(suan)功(gong)能。為便于(yu)負載均衡、數(shu)(shu)據(ju)恢復(fu)、支持(chi)異(yi)構(gou)環(huan)境,TDengine 將一個數(shu)(shu)據(ju)節點(dian)(dian)根據(ju)其計算(suan)和(he)(he)存儲資源(yuan)切(qie)分為多個 vnode。這些 vnode 的(de)管理是TDengine 自動完成的(de),對應用完全透(tou)明(ming)。
對于(yu)單獨一(yi)(yi)(yi)(yi)個(ge)數(shu)(shu)(shu)據(ju)采(cai)(cai)(cai)集(ji)點(dian),無論其(qi)數(shu)(shu)(shu)據(ju)量多(duo)大,一(yi)(yi)(yi)(yi)個(ge) vnode(或(huo) vnode group, 如果副本數(shu)(shu)(shu)大于(yu) 1)有足夠的(de)(de)計算資(zi)源和存儲資(zi)源來處理(如果每秒(miao)生(sheng)(sheng)成一(yi)(yi)(yi)(yi)條(tiao) 16 字節的(de)(de)記錄,一(yi)(yi)(yi)(yi)年產生(sheng)(sheng)的(de)(de)原始數(shu)(shu)(shu)據(ju)不(bu)(bu)到(dao) 0.5G),因此 TDengine 將一(yi)(yi)(yi)(yi)張表(biao)(一(yi)(yi)(yi)(yi)個(ge)數(shu)(shu)(shu)據(ju)采(cai)(cai)(cai)集(ji)點(dian))的(de)(de)所有數(shu)(shu)(shu)據(ju)都(dou)存放在一(yi)(yi)(yi)(yi)個(ge) vnode 里(li),而不(bu)(bu)會讓同一(yi)(yi)(yi)(yi)個(ge)采(cai)(cai)(cai)集(ji)點(dian)的(de)(de)數(shu)(shu)(shu)據(ju)分布到(dao)兩(liang)個(ge)或(huo)多(duo)個(ge) dnode 上(shang)。而且一(yi)(yi)(yi)(yi)個(ge) vnode 可存儲多(duo)個(ge)數(shu)(shu)(shu)據(ju)采(cai)(cai)(cai)集(ji)點(dian)(表(biao))的(de)(de)數(shu)(shu)(shu)據(ju),一(yi)(yi)(yi)(yi)個(ge) vnode 可容納的(de)(de)表(biao)的(de)(de)數(shu)(shu)(shu)目的(de)(de)上(shang)限為(wei)一(yi)(yi)(yi)(yi)百萬。設計上(shang),一(yi)(yi)(yi)(yi)個(ge) vnode 里(li)所有的(de)(de)表(biao)都(dou)屬于(yu)同一(yi)(yi)(yi)(yi)個(ge) DB。一(yi)(yi)(yi)(yi)個(ge)數(shu)(shu)(shu)據(ju)節點(dian)上(shang),除非特殊配置,一(yi)(yi)(yi)(yi)個(ge) DB 擁有的(de)(de) vnode 數(shu)(shu)(shu)目不(bu)(bu)會超過系統(tong)核的(de)(de)數(shu)(shu)(shu)目。
創(chuang)建(jian) DB 時(shi),系(xi)(xi)(xi)(xi)統并不會(hui)馬上分(fen)配資源。但當(dang)(dang)創(chuang)建(jian)一(yi)張(zhang)表時(shi),系(xi)(xi)(xi)(xi)統將看是(shi)否(fou)有(you)已經分(fen)配的 vnode, 且該 vnode 是(shi)否(fou)有(you)空余的表空間,如果(guo)有(you),立即在該有(you)空位的 vnode 創(chuang)建(jian)表。如果(guo)沒有(you),系(xi)(xi)(xi)(xi)統將從集群(qun)中,根據當(dang)(dang)前的負載情況,在一(yi)個(ge) dnode 上創(chuang)建(jian)一(yi)新的 vnode, 然后創(chuang)建(jian)表。如果(guo)DB有(you)多個(ge)副本(ben),系(xi)(xi)(xi)(xi)統不是(shi)只創(chuang)建(jian)一(yi)個(ge) vnode,而是(shi)一(yi)個(ge) vgroup (虛擬數(shu)據節(jie)點組)。系(xi)(xi)(xi)(xi)統對(dui) vnode 的數(shu)目沒有(you)任何限(xian)制,僅僅受限(xian)于物(wu)理節(jie)點本(ben)身的計算和存儲資源。
每張表的(de) meta data(包含(han) schema, 標(biao)簽等)也存(cun)放于(yu)(yu) vnode 里,而不是集中存(cun)放于(yu)(yu) mnode,實際上這是對(dui) Meta 數據的(de)分片,這樣便于(yu)(yu)高(gao)效并行的(de)進行標(biao)簽過濾操作。
數據分區
TDengine 除(chu) vnode 分片(pian)之外,還對時(shi)(shi)序數(shu)(shu)據(ju)(ju)按照(zhao)時(shi)(shi)間段(duan)(duan)進行分區。每個數(shu)(shu)據(ju)(ju)文件只(zhi)包(bao)含(han)一個時(shi)(shi)間段(duan)(duan)的(de)(de)(de)時(shi)(shi)序數(shu)(shu)據(ju)(ju),時(shi)(shi)間段(duan)(duan)的(de)(de)(de)長度由 DB 的(de)(de)(de)配置參數(shu)(shu) days 決定。這種按時(shi)(shi)間段(duan)(duan)分區的(de)(de)(de)方法還便(bian)于(yu)(yu)高(gao)效實現數(shu)(shu)據(ju)(ju)的(de)(de)(de)保留策略,只(zhi)要數(shu)(shu)據(ju)(ju)文件超過規定的(de)(de)(de)天(tian)數(shu)(shu)(系(xi)統配置參數(shu)(shu) keep),將被自動刪除(chu)。而且不同的(de)(de)(de)時(shi)(shi)間段(duan)(duan)可(ke)以(yi)存(cun)放于(yu)(yu)不同的(de)(de)(de)路徑(jing)和(he)存(cun)儲介(jie)質,以(yi)便(bian)于(yu)(yu)大(da)數(shu)(shu)據(ju)(ju)的(de)(de)(de)冷熱管理,實現多級存(cun)儲。
總的來說,TDengine 是通過 vnode 以及時間兩個維度,對大數據進行切分,便于并行高效的管理(li),實現水平擴展。
負載均衡
每個(ge) dnode 都定時向 mnode(虛擬管理節點(dian))報告其狀態(包括硬(ying)盤(pan)空間、內存(cun)大(da)小、CPU、網絡、虛擬節點(dian)個(ge)數等),因此 mnode 了解整(zheng)(zheng)個(ge)集群的狀態。基于整(zheng)(zheng)體狀態,當 mnode 發現某個(ge)dnode負載過重,它會(hui)將(jiang)dnode 上的一個(ge)或多(duo)個(ge) vnode 挪(nuo)到(dao)其他 dnode。在挪(nuo)動過程(cheng)中,對外(wai)服(fu)務(wu)繼續進(jin)行,數據插入(ru)、查詢(xun)和計算操作(zuo)都不受(shou)影響。
如(ru)(ru)(ru)果(guo)(guo) mnode 一(yi)段時(shi)間沒(mei)有收到(dao) dnode 的狀態報(bao)告,mnode 會認為這個 dnode 已經離(li)線。如(ru)(ru)(ru)果(guo)(guo)離(li)線時(shi)間超過一(yi)定(ding)時(shi)長(時(shi)長由配置參數(shu) offlineThreshold 決定(ding)),該 dnode 將被 mnode 強制剔除出集群。該dnode 上的 vnodes 如(ru)(ru)(ru)果(guo)(guo)副(fu)(fu)本(ben)(ben)數(shu)大(da)于 1,系(xi)統(tong)將自(zi)(zi)動在其他(ta) dnode 上創建新的副(fu)(fu)本(ben)(ben),以保證數(shu)據的副(fu)(fu)本(ben)(ben)數(shu)。如(ru)(ru)(ru)果(guo)(guo)該 dnode 上還有 mnode, 而且 mnode 的副(fu)(fu)本(ben)(ben)數(shu)大(da)于1,系(xi)統(tong)也(ye)將自(zi)(zi)動在其他(ta) dnode 上創建新的 mnode, 以保證 mnode 的副(fu)(fu)本(ben)(ben)數(shu)。
當新(xin)的數據節點被添(tian)加進集群,因(yin)為新(xin)的計(ji)算和存(cun)儲被添(tian)加進來(lai),系統也將自動啟動負載均衡(heng)流(liu)程(cheng)。
負(fu)載均衡(heng)過程(cheng)無(wu)需任何(he)人工干(gan)預(yu),應(ying)用也(ye)無(wu)需重啟,將自動連接新的節(jie)點(dian),完全透明。
提示:負載均衡由參數 balance 控制,決定開啟/關閉自動負載均衡。
數據寫入與復制流程
如果一個(ge)數(shu)據庫(ku)有 N 個(ge)副本,那一個(ge)虛擬節點組就(jiu)有 N 個(ge)虛擬節點,但(dan)是(shi)只有一個(ge)是(shi) master,其他(ta)都(dou)是(shi) slave。當(dang)應用將(jiang)新的(de)記錄(lu)寫(xie)(xie)入系統(tong)時,只有 master vnode 能接受寫(xie)(xie)的(de)請求。如果 slave vnode 收到寫(xie)(xie)的(de)請求,系統(tong)將(jiang)通(tong)知(zhi) taosc 需(xu)要重新定向。
Master Vnode 寫入流程
Master Vnode 遵(zun)循下(xia)面(mian)的寫入流(liu)程(cheng):

- master vnode 收到應用的數據插入請求,驗證OK,進入下一步;
- 如果系統配置參數 walLevel 大于 0,vnode 將把該請求的原始數據包寫入數據庫日志文件 WAL。如果 walLevel 設置為 2,而且 fsync 設置為 0,TDengine 還將 WAL 數據立即落盤,以保證即使宕機,也能從數據庫日志文件中恢復數據,避免數據的丟失;
- 如果有多個副本,vnode 將把數據包轉發給同一虛擬節點組內的 slave vnodes, 該轉發包帶有數據的版本號(version);
- 寫入內存,并將記錄加入到 skip list;
- master vnode 返回確認信息給應用,表示寫入成功。
- 如果第 2、3、4 步中任何一步失敗,將直接返回錯誤給應用。
Slave Vnode 寫入流程
對(dui)于 slave vnode,寫入流程是:

- slave vnode 收到 Master vnode 轉發了的數據插入請求。檢查 last version 是否與 master 一致,如果一致,進入下一步。如果不一致,需要進入同步狀態。
- 如果系統配置參數 walLevel 大于 0,vnode 將把該請求的原始數據包寫入數據庫日志文件 WAL。如果 walLevel 設置為 2,而且 fsync 設置為 0,TDengine 還將 WAL 數據立即落盤,以保證即使宕機,也能從數據庫日志文件中恢復數據,避免數據的丟失。
- 寫入內存,更新內存中的 skip list。
與 master vnode 相比,slave vnode 不存(cun)在(zai)轉(zhuan)發(fa)環(huan)節,也不存(cun)在(zai)回(hui)復確認環(huan)節,少(shao)了兩步。但寫內存(cun)與 WAL 是完全一樣(yang)的。
主從選擇
Vnode 會保持一個(ge)數(shu)據(ju)(ju)版本號(version),對內存數(shu)據(ju)(ju)進行持久化存儲(chu)時(shi),對該版本號也進行持久化存儲(chu)。每個(ge)數(shu)據(ju)(ju)更新操作,無論是采集的時(shi)序(xu)數(shu)據(ju)(ju)還是元數(shu)據(ju)(ju),這(zhe)個(ge)版本號將(jiang)增加 1。
一(yi)個 vnode 啟動時(shi),角(jiao)色(master、slave) 是不定的,數據是處于未同步(bu)狀態,它需要與(yu)虛擬節點組內其他節點建立(li) TCP 連接,并互相交(jiao)換(huan) status,其中包括 version 和自己(ji)的角(jiao)色。通過 status 的交(jiao)換(huan),系統進入選主流程(cheng),規則如下(xia):
- 如果只有一個副本,該副本永遠就是 master
- 所有副本都在線時,版本最高的被選為 master
- 在線的虛擬節點數過半,而且有虛擬節點是 slave 的話,該虛擬節點自動成為 master
- 對于 2 和 3,如果多個虛擬節點滿足成為 master 的要求,那么虛擬節點組的節點列表里,最前面的選為 master
更多的關于數據復制的流程,請見TDengine 2.0 數據復制模塊設計。
同步復制
對于(yu)數(shu)(shu)據一(yi)致性要求更高的(de)場景,異步數(shu)(shu)據復制無法(fa)滿足要求,因(yin)(yin)為有(you)極小的(de)概率丟失數(shu)(shu)據,因(yin)(yin)此 TDengine 提供(gong)同步復制的(de)機(ji)制供(gong)用(yong)(yong)戶選擇。在(zai)創建(jian)數(shu)(shu)據庫(ku)時,除指(zhi)定(ding)副(fu)(fu)本(ben)數(shu)(shu) replica 之外,用(yong)(yong)戶還(huan)需(xu)要指(zhi)定(ding)新(xin)的(de)參數(shu)(shu) quorum。如果 quorum 大于(yu)1,它表示每次(ci)master轉發給副(fu)(fu)本(ben)時,需(xu)要等(deng)待 quorum-1 個(ge)回復確(que)認,才能(neng)通(tong)知應(ying)用(yong)(yong),數(shu)(shu)據在(zai) slave 已(yi)經寫(xie)入成功。如果在(zai)一(yi)定(ding)的(de)時間內(nei),得不到(dao) quorum-1 個(ge)回復確(que)認,master vnode 將返回錯誤給應(ying)用(yong)(yong)。
采用(yong)同(tong)(tong)步復(fu)制(zhi),系統的性能會有所(suo)下降,而且(qie) latency 會增加。因為元數(shu)據(ju)(ju)要(yao)強一致(zhi),mnode 之間(jian)的數(shu)據(ju)(ju)同(tong)(tong)步缺省就(jiu)是采用(yong)的同(tong)(tong)步復(fu)制(zhi)。
緩存與持久化
緩存
TDengine 采用(yong)時間(jian)驅(qu)動緩存(cun)(cun)管(guan)理策略(lve)(First-In-First-Out,FIFO),又稱為寫(xie)驅(qu)動的(de)緩存(cun)(cun)管(guan)理機制。這種策略(lve)有別于讀驅(qu)動的(de)數(shu)據緩存(cun)(cun)模(mo)式(Least-Recent-Used,LRU),直接將(jiang)(jiang)最近寫(xie)入(ru)的(de)數(shu)據保存(cun)(cun)在系(xi)統的(de)緩存(cun)(cun)中(zhong)。當緩存(cun)(cun)達(da)到臨界值的(de)時候,將(jiang)(jiang)最早的(de)數(shu)據批量寫(xie)入(ru)磁盤。一般意義(yi)上來(lai)說(shuo),對(dui)于物聯網(wang)數(shu)據的(de)使用(yong),用(yong)戶最為關心(xin)的(de)是剛(gang)產生(sheng)的(de)數(shu)據,即當前狀(zhuang)態。TDengine 充分(fen)利用(yong)這一特性,將(jiang)(jiang)最近到達(da)的(de)(當前狀(zhuang)態)數(shu)據保存(cun)(cun)在緩存(cun)(cun)中(zhong)。
TDengine 通過查詢函數向用戶提供毫秒級的數據獲取能力。直接將最近到達的數據保存在緩存中,可以更加快速地響應用戶針對最近一條或一批數據的查詢分析,整體上提供更快的數據庫查詢響應能力。從這個意義上來說,可通過設置合適的配置參數將 TDengine 作為數據緩存來使用,而不需要再部署 Redis 或其他額外的緩存系統,可有效(xiao)地簡化系統架構,降低運(yun)維的(de)(de)成(cheng)本。需(xu)要注意的(de)(de)是,TDengine 重啟以后系統的(de)(de)緩(huan)(huan)(huan)存將被清空(kong),之前緩(huan)(huan)(huan)存的(de)(de)數(shu)據(ju)均會被批量(liang)寫入磁盤,緩(huan)(huan)(huan)存的(de)(de)數(shu)據(ju)將不會像(xiang)專門(men)的(de)(de) key-value 緩(huan)(huan)(huan)存系統再將之前緩(huan)(huan)(huan)存的(de)(de)數(shu)據(ju)重新(xin)加載到(dao)緩(huan)(huan)(huan)存中。
每(mei)個(ge) vnode 有(you)(you)自己獨立的(de)(de)內(nei)(nei)(nei)(nei)(nei)存(cun),而(er)且(qie)由(you)(you)多個(ge)固定(ding)(ding)大(da)小的(de)(de)內(nei)(nei)(nei)(nei)(nei)存(cun)塊(kuai)(kuai)組成,不同 vnode 之間完全隔離。數(shu)據(ju)寫入時,類似(si)于日志的(de)(de)寫法,數(shu)據(ju)被順序(xu)追加寫入內(nei)(nei)(nei)(nei)(nei)存(cun),但每(mei)個(ge) vnode 維護有(you)(you)自己的(de)(de) skip list,便于迅速查(cha)找。當三(san)分之一(yi)以(yi)上的(de)(de)內(nei)(nei)(nei)(nei)(nei)存(cun)塊(kuai)(kuai)寫滿時,啟動落盤操(cao)作,而(er)且(qie)后續寫的(de)(de)操(cao)作在新的(de)(de)內(nei)(nei)(nei)(nei)(nei)存(cun)塊(kuai)(kuai)進(jin)行。這樣,一(yi)個(ge) vnode 里(li)有(you)(you)三(san)分之一(yi)內(nei)(nei)(nei)(nei)(nei)存(cun)塊(kuai)(kuai)是(shi)保(bao)留(liu)有(you)(you)最近的(de)(de)數(shu)據(ju)的(de)(de),以(yi)達到緩存(cun)、快速查(cha)找的(de)(de)目(mu)的(de)(de)。一(yi)個(ge) vnode 的(de)(de)內(nei)(nei)(nei)(nei)(nei)存(cun)塊(kuai)(kuai)的(de)(de)個(ge)數(shu)由(you)(you)配置參數(shu) blocks 決(jue)定(ding)(ding),內(nei)(nei)(nei)(nei)(nei)存(cun)塊(kuai)(kuai)的(de)(de)大(da)小由(you)(you)配置參數(shu) cache 決(jue)定(ding)(ding)。
持久化存儲
TDengine 采(cai)用數(shu)據(ju)(ju)(ju)(ju)(ju)驅(qu)動(dong)的(de)方式(shi)讓(rang)緩(huan)存(cun)中的(de)數(shu)據(ju)(ju)(ju)(ju)(ju)寫(xie)入硬盤(pan)進行(xing)持久化(hua)存(cun)儲(chu)。當 vnode 中緩(huan)存(cun)的(de)數(shu)據(ju)(ju)(ju)(ju)(ju)達到一定規模時(shi),為了不阻塞后續數(shu)據(ju)(ju)(ju)(ju)(ju)的(de)寫(xie)入,TDengine 也(ye)會拉(la)起(qi)落盤(pan)線程將緩(huan)存(cun)的(de)數(shu)據(ju)(ju)(ju)(ju)(ju)寫(xie)入持久化(hua)存(cun)儲(chu)。TDengine 在數(shu)據(ju)(ju)(ju)(ju)(ju)落盤(pan)時(shi)會打(da)開新的(de)數(shu)據(ju)(ju)(ju)(ju)(ju)庫日(ri)(ri)志文件,在落盤(pan)成(cheng)功后則會刪(shan)除老的(de)數(shu)據(ju)(ju)(ju)(ju)(ju)庫日(ri)(ri)志文件,避免日(ri)(ri)志文件無(wu)限(xian)制地(di)增長。
為充分利用(yong)時序數據(ju)特點,TDengine 將一個(ge) vnode 保存在(zai)持久化存儲(chu)的(de)數據(ju)切分成多個(ge)文件,每個(ge)文件只(zhi)保存固定天(tian)數的(de)數據(ju),這(zhe)個(ge)天(tian)數由(you)系統(tong)配置參數 days 決定。切分成多個(ge)文件后,給定查詢的(de)起(qi)止日期,無需任何(he)索(suo)引,就可以立即定位需要打(da)開哪些(xie)數據(ju)文件,大大加快讀取(qu)速度(du)。
對于采集的數(shu)據,一(yi)般有(you)保(bao)留時(shi)長(chang)(chang),這(zhe)個(ge)時(shi)長(chang)(chang)由系統(tong)配置(zhi)參數(shu) keep 決(jue)定。超(chao)過這(zhe)個(ge)設(she)置(zhi)天數(shu)的數(shu)據文件,將被系統(tong)自動刪除,釋放存儲空間(jian)。
給定 days 與 keep 兩個參數,一個典型工作狀態的 vnode 中總的數據文件數為:向上取整(keep/days)+1個(ge)。總的數(shu)據文件個(ge)數(shu)不(bu)宜過大,也(ye)不(bu)宜過小(xiao)。10到100以內合適。基于(yu)這(zhe)個(ge)原(yuan)則,可以設置合理的 days。目(mu)前的版本,參(can)數(shu) keep 可以修(xiu)改,但對于(yu)參(can)數(shu) days,一旦(dan)設置后,不(bu)可修(xiu)改。
在每(mei)個(ge)數(shu)(shu)(shu)據(ju)(ju)文件(jian)(jian)(jian)里,一張(zhang)表的(de)(de)數(shu)(shu)(shu)據(ju)(ju)是一塊一塊存儲的(de)(de)。一張(zhang)表可以有一到多個(ge)數(shu)(shu)(shu)據(ju)(ju)文件(jian)(jian)(jian)塊。在一個(ge)文件(jian)(jian)(jian)塊里,數(shu)(shu)(shu)據(ju)(ju)是列(lie)式存儲的(de)(de),占用的(de)(de)是一片連續的(de)(de)存儲空間,這樣大(da)大(da)提高讀(du)取(qu)速(su)(su)度。文件(jian)(jian)(jian)塊的(de)(de)大(da)小(xiao)由系統參(can)數(shu)(shu)(shu) maxRows (每(mei)塊最大(da)記錄條數(shu)(shu)(shu))決定,缺(que)省值為 4096。這個(ge)值不(bu)宜過(guo)大(da),也不(bu)宜過(guo)小(xiao)。過(guo)大(da),定位(wei)具體時間段的(de)(de)數(shu)(shu)(shu)據(ju)(ju)的(de)(de)搜(sou)索時間會變(bian)長(chang),影響(xiang)讀(du)取(qu)速(su)(su)度;過(guo)小(xiao),數(shu)(shu)(shu)據(ju)(ju)塊的(de)(de)索引太大(da),壓縮(suo)效率偏低,也影響(xiang)讀(du)取(qu)速(su)(su)度。
每(mei)個(ge)數(shu)(shu)據(ju)(ju)(ju)文件(jian)(.data 結(jie)尾)都有一(yi)(yi)(yi)個(ge)對(dui)應的(de)(de)索引(yin)文件(jian)(.head 結(jie)尾),該(gai)索引(yin)文件(jian)對(dui)每(mei)張(zhang)表(biao)都有一(yi)(yi)(yi)數(shu)(shu)據(ju)(ju)(ju)塊的(de)(de)摘要(yao)信息(xi),記(ji)(ji)錄(lu)(lu)了每(mei)個(ge)數(shu)(shu)據(ju)(ju)(ju)塊在數(shu)(shu)據(ju)(ju)(ju)文件(jian)中的(de)(de)偏(pian)移量,數(shu)(shu)據(ju)(ju)(ju)的(de)(de)起止時(shi)間等(deng)信息(xi),以幫(bang)助系統迅(xun)速(su)定(ding)位需要(yao)查找(zhao)的(de)(de)數(shu)(shu)據(ju)(ju)(ju)。每(mei)個(ge)數(shu)(shu)據(ju)(ju)(ju)文件(jian)還有一(yi)(yi)(yi)對(dui)應的(de)(de) last 文件(jian)(.last 結(jie)尾),該(gai)文件(jian)是為(wei)防止落(luo)盤(pan)時(shi)數(shu)(shu)據(ju)(ju)(ju)塊碎片化而設(she)計的(de)(de)。如果一(yi)(yi)(yi)張(zhang)表(biao)落(luo)盤(pan)的(de)(de)記(ji)(ji)錄(lu)(lu)條數(shu)(shu)沒有達到(dao)系統配置參(can)數(shu)(shu) minRows(每(mei)塊最小記(ji)(ji)錄(lu)(lu)條數(shu)(shu)),將被(bei)先存儲到(dao) last 文件(jian),等(deng)下次落(luo)盤(pan)時(shi),新落(luo)盤(pan)的(de)(de)記(ji)(ji)錄(lu)(lu)將與 last 文件(jian)的(de)(de)記(ji)(ji)錄(lu)(lu)進行合(he)并,再寫入數(shu)(shu)據(ju)(ju)(ju)文件(jian)。
數據(ju)(ju)寫(xie)入磁盤時,根(gen)據(ju)(ju)系統配置參數 comp 決定是否壓(ya)(ya)(ya)縮(suo)數據(ju)(ju)。TDengine 提供了(le)三種壓(ya)(ya)(ya)縮(suo)選項:無(wu)壓(ya)(ya)(ya)縮(suo)、一階(jie)(jie)(jie)段(duan)壓(ya)(ya)(ya)縮(suo)和(he)兩階(jie)(jie)(jie)段(duan)壓(ya)(ya)(ya)縮(suo),分別對應 comp 值(zhi)為 0、1 和(he) 2 的(de)情況。一階(jie)(jie)(jie)段(duan)壓(ya)(ya)(ya)縮(suo)根(gen)據(ju)(ju)數據(ju)(ju)的(de)類(lei)型進(jin)行了(le)相應的(de)壓(ya)(ya)(ya)縮(suo),壓(ya)(ya)(ya)縮(suo)算法包(bao)括 delta-delta 編(bian)碼、simple 8B 方法、zig-zag 編(bian)碼、LZ4 等算法。二階(jie)(jie)(jie)段(duan)壓(ya)(ya)(ya)縮(suo)在一階(jie)(jie)(jie)段(duan)壓(ya)(ya)(ya)縮(suo)的(de)基礎(chu)上又(you)用通用壓(ya)(ya)(ya)縮(suo)算法進(jin)行了(le)壓(ya)(ya)(ya)縮(suo),壓(ya)(ya)(ya)縮(suo)率更(geng)高。
多級存儲
說明:多級存儲功能僅企業版支持,從 2.0.16.0 版本開始(shi)提供。
在默認配置下(xia),TDengine 會將所有數(shu)據保存在 /var/lib/taos 目錄下(xia),而且每(mei)個 vnode 的(de)(de)數(shu)據文(wen)件保存在該目錄下(xia)的(de)(de)不同(tong)(tong)目錄。為擴(kuo)大存儲空間,盡量減少文(wen)件讀取的(de)(de)瓶頸(jing),提高數(shu)據吞吐率(lv) TDengine 可通過配置系統(tong)參數(shu) dataDir 讓多(duo)個掛(gua)載(zai)的(de)(de)硬盤被系統(tong)同(tong)(tong)時使(shi)用。
除此之外,TDengine 也提供了數據(ju)(ju)分(fen)級存(cun)儲(chu)的(de)(de)功能(neng),將(jiang)(jiang)不同(tong)時間段(duan)的(de)(de)數據(ju)(ju)存(cun)儲(chu)在(zai)掛載的(de)(de)不同(tong)介質(zhi)上的(de)(de)目(mu)錄里,從(cong)而實現不同(tong)“熱度(du)”的(de)(de)數據(ju)(ju)存(cun)儲(chu)在(zai)不同(tong)的(de)(de)存(cun)儲(chu)介質(zhi)上,充分(fen)利用存(cun)儲(chu),節約成(cheng)本(ben)。比(bi)如(ru),最(zui)新(xin)采集的(de)(de)數據(ju)(ju)需要經常訪(fang)問(wen),對硬盤(pan)的(de)(de)讀取性能(neng)要求高,那(nei)(nei)么用戶可(ke)以配置將(jiang)(jiang)這些數據(ju)(ju)存(cun)儲(chu)在(zai) SSD 盤(pan)上。超過一定期限的(de)(de)數據(ju)(ju),查詢需求量沒有那(nei)(nei)么高,那(nei)(nei)么可(ke)以存(cun)儲(chu)在(zai)相對便宜(yi)的(de)(de) HDD 盤(pan)上。
多級存(cun)儲支持3級,每級最多可配置 16 個掛載點。
TDengine 多級存儲(chu)配置方式如下(在配置文(wen)件(jian)/etc/taos/taos.cfg中):
dataDir [path] <level> <primary>
- path: 掛載點的文件夾路徑
- level: 介質存儲等級,取值為 0,1,2。
0級存儲最新的數據,1級存儲次新的數據,2級存儲最老的數據,省略默認為 0。
各級存儲之間的數據流向:0 級存儲 -> 1 級存儲 -> 2 級存儲。
同一存儲等級可掛載多個硬盤,同一存儲等級上的數據文件分布在該存儲等級的所有硬盤上。
需要說明的是,數據在不同級別的存儲介質上的移動,是由系統自動完成的,用戶無需干預。 - primary: 是否為主掛載點,0(否)或 1(是),省略默認為 1。
在(zai)配置中,只允許一個主掛載點的(de)存在(zai)(level=0, primary=1),例(li)如采用如下的(de)配置方(fang)式:
dataDir /mnt/data1 0 1
dataDir /mnt/data2 0 0
dataDir /mnt/data3 1 0
dataDir /mnt/data4 1 0
dataDir /mnt/data5 2 0
dataDir /mnt/data6 2 0
注意:
- 多級存儲不允許跨級配置,合法的配置方案有:僅 0 級,僅 0 級+ 1 級,以及 0 級+ 1 級+ 2 級。而不允許只配置 level=0 和 level=2,而不配置 level=1。
- 禁止手動移除使用中的掛載盤,掛載盤目前不支持非本地的網絡盤。
- 多級存儲目前不支持刪除已經掛載的硬盤的功能。
數據查詢
TDengine 提供了(le)多種多樣針(zhen)對表和超(chao)級表的查(cha)(cha)詢(xun)(xun)處理(li)功能,除(chu)了(le)常規的聚合查(cha)(cha)詢(xun)(xun)之外(wai),還提供針(zhen)對時序數據的窗口查(cha)(cha)詢(xun)(xun)、統(tong)計聚合等功能。TDengine 的查(cha)(cha)詢(xun)(xun)處理(li)需要客戶端、vnode、mnode 節點協(xie)同完成。
單表查詢
SQL 語(yu)句的(de)解(jie)析(xi)和(he)校驗工(gong)作(zuo)在客戶(hu)端完成。解(jie)析(xi) SQL 語(yu)句并生成抽象(xiang)語(yu)法樹(Abstract Syntax Tree, AST),然后對其進行校驗和(he)檢查(cha)。以及向(xiang)管理節點(mnode)請求(qiu)查(cha)詢中指(zhi)定(ding)表的(de)元數據信息(table metadata)。
根據(ju)元數據(ju)信(xin)息(xi)中(zhong)的 End Point 信(xin)息(xi),將(jiang)查(cha)(cha)詢(xun)(xun)(xun)請求(qiu)(qiu)序列(lie)化后(hou)發送(song)到(dao)該(gai)表所在(zai)的數據(ju)節點(dnode)。dnode 接收到(dao)查(cha)(cha)詢(xun)(xun)(xun)請求(qiu)(qiu)后(hou),識別出該(gai)查(cha)(cha)詢(xun)(xun)(xun)請求(qiu)(qiu)指向的虛(xu)擬節點(vnode),將(jiang)消息(xi)轉發到(dao) vnode 的查(cha)(cha)詢(xun)(xun)(xun)執(zhi)行(xing)隊列(lie)。vnode 的查(cha)(cha)詢(xun)(xun)(xun)執(zhi)行(xing)線(xian)程建立基礎(chu)的查(cha)(cha)詢(xun)(xun)(xun)執(zhi)行(xing)環境,并立即返回該(gai)查(cha)(cha)詢(xun)(xun)(xun)請求(qiu)(qiu),同(tong)時開始執(zhi)行(xing)該(gai)查(cha)(cha)詢(xun)(xun)(xun)。
客戶端在獲取查(cha)詢(xun)(xun)結(jie)果的(de)時候,dnode 的(de)查(cha)詢(xun)(xun)執行隊列中的(de)工作(zuo)線(xian)程會(hui)等待 vnode 執行線(xian)程執行完成,才(cai)能將查(cha)詢(xun)(xun)結(jie)果返(fan)回到(dao)請求(qiu)的(de)客戶端。
按時間軸聚合、降采樣、插值
時(shi)序數(shu)(shu)據(ju)有(you)別于普通(tong)(tong)數(shu)(shu)據(ju)的顯著特征是每條(tiao)記錄均具有(you)時(shi)間戳,因此針對具有(you)時(shi)間戳的數(shu)(shu)據(ju)在(zai)時(shi)間軸上進行(xing)聚合是不同于普通(tong)(tong)數(shu)(shu)據(ju)庫的重(zhong)要功能。從(cong)這點上來看(kan),與流計(ji)算(suan)引擎的窗口查詢有(you)相似的地方。
在 TDengine 中引入關鍵詞 interval 來進(jin)行時(shi)(shi)間軸上固(gu)定長度時(shi)(shi)間窗口(kou)的切分,并按照時(shi)(shi)間窗口(kou)對(dui)數(shu)據(ju)進(jin)行聚合(he),對(dui)窗口(kou)范圍(wei)內的數(shu)據(ju)按需進(jin)行聚合(he)。例如:
SELECT COUNT(*) FROM d1001 INTERVAL(1h);
針對 d1001 設備采(cai)集的(de)(de)數據,按照(zhao)1小時(shi)的(de)(de)時(shi)間窗(chuang)口返回每小時(shi)存儲的(de)(de)記錄數量。
在需要連續(xu)獲得查(cha)詢結(jie)果(guo)的(de)應用場景下,如(ru)果(guo)給定的(de)時間(jian)區(qu)間(jian)存在數據(ju)缺失(shi),會(hui)導致該(gai)區(qu)間(jian)數據(ju)結(jie)果(guo)也丟(diu)失(shi)。TDengine 提(ti)供策略針對時間(jian)軸(zhou)聚(ju)合計(ji)算的(de)結(jie)果(guo)進行(xing)插(cha)(cha)值(zhi),通過使用關鍵詞(ci) fill 就能夠對時間(jian)軸(zhou)聚(ju)合結(jie)果(guo)進行(xing)插(cha)(cha)值(zhi)。例(li)如(ru):
SELECT COUNT(*) FROM d1001 WHERE ts >= '2017-7-14 00:00:00' AND ts < '2017-7-14 23:59:59' INTERVAL(1h) FILL(PREV);
針對 d1001 設備采集(ji)數(shu)據(ju)統(tong)計每小(xiao)(xiao)時(shi)記錄數(shu),如(ru)果某一個(ge)小(xiao)(xiao)時(shi)不存在數(shu)據(ju),則返(fan)回(hui)之(zhi)前一個(ge)小(xiao)(xiao)時(shi)的(de)統(tong)計數(shu)據(ju)。TDengine 提供前向插值(prev)、線性插值(linear)、NULL值填充(chong)(NULL)、特定值填充(chong)(value)。
多表聚合查詢
TDengine 對(dui)每(mei)(mei)個數(shu)據采(cai)(cai)集(ji)點(dian)單獨(du)建(jian)表(biao)(biao),但在實(shi)際應用(yong)(yong)中經常(chang)需要(yao)對(dui)不同的(de)采(cai)(cai)集(ji)點(dian)數(shu)據進(jin)行(xing)聚合(he)(he)。為高(gao)效的(de)進(jin)行(xing)聚合(he)(he)操作,TDengine 引入(ru)超(chao)級表(biao)(biao)(STable)的(de)概念。超(chao)級表(biao)(biao)用(yong)(yong)來代表(biao)(biao)一特(te)定(ding)類型的(de)數(shu)據采(cai)(cai)集(ji)點(dian),它是包含(han)多張(zhang)表(biao)(biao)的(de)表(biao)(biao)集(ji)合(he)(he),集(ji)合(he)(he)里每(mei)(mei)張(zhang)表(biao)(biao)的(de)模(mo)式(schema)完全(quan)一致,但每(mei)(mei)張(zhang)表(biao)(biao)都帶(dai)有自己的(de)靜(jing)態標簽,標簽可以(yi)有多個,可以(yi)隨時增(zeng)加、刪(shan)除和修改。應用(yong)(yong)可通過(guo)指定(ding)標簽的(de)過(guo)濾條件,對(dui)一個 STable 下的(de)全(quan)部(bu)或部(bu)分表(biao)(biao)進(jin)行(xing)聚合(he)(he)或統計操作,這(zhe)樣大大簡化(hua)應用(yong)(yong)的(de)開(kai)發。其具體流程如下圖(tu)所示:

- 應用將一個查詢條件發往系統;
- taosc 將超級表的名字發往 meta node(管理節點);
- 管理節點將超級表所擁有的 vnode 列表發回 taosc;
- taosc 將計算的請求連同標簽過濾條件發往這些 vnode 對應的多個數據節點;
- 每個 vnode 先在內存里查找出自己節點里符合標簽過濾條件的表的集合,然后掃描存儲的時序數據,完成相應的聚合計算,將結果返回給 taosc;
- taosc 將多個數據節點返回的結果做最后的聚合,將其返回給應用。
由于 TDengine 在(zai) vnode 內將標簽(qian)數(shu)據(ju)(ju)與(yu)時序(xu)數(shu)據(ju)(ju)分(fen)離存儲,通(tong)過(guo)(guo)在(zai)內存里(li)過(guo)(guo)濾(lv)標簽(qian)數(shu)據(ju)(ju),先找到(dao)需要(yao)(yao)參(can)與(yu)聚(ju)合(he)操(cao)作(zuo)(zuo)的(de)表的(de)集(ji)合(he),將需要(yao)(yao)掃描的(de)數(shu)據(ju)(ju)集(ji)大(da)幅減少,大(da)幅提(ti)(ti)升(sheng)聚(ju)合(he)計算速度(du)。同時,由于數(shu)據(ju)(ju)分(fen)布在(zai)多個 vnode/dnode,聚(ju)合(he)計算操(cao)作(zuo)(zuo)在(zai)多個 vnode 里(li)并發進行,又(you)進一步提(ti)(ti)升(sheng)了聚(ju)合(he)的(de)速度(du)。 對普通(tong)表的(de)聚(ju)合(he)函數(shu)以及(ji)絕(jue)大(da)部分(fen)操(cao)作(zuo)(zuo)都適(shi)用(yong)于超(chao)級(ji)表,語法完(wan)全一樣,細節請看 TAOS SQL。
預計算
為(wei)有效提升查詢(xun)處(chu)(chu)理的(de)(de)性(xing)能,針對物聯(lian)網數據(ju)(ju)(ju)(ju)的(de)(de)不可更改(gai)的(de)(de)特(te)點,在數據(ju)(ju)(ju)(ju)塊頭部(bu)記錄該數據(ju)(ju)(ju)(ju)塊中存(cun)儲數據(ju)(ju)(ju)(ju)的(de)(de)統計(ji)(ji)(ji)信息:包(bao)括最大值(zhi)、最小(xiao)值(zhi)、和。我(wo)們稱之(zhi)為(wei)預(yu)計(ji)(ji)(ji)算單(dan)元。如果查詢(xun)處(chu)(chu)理涉(she)及整個數據(ju)(ju)(ju)(ju)塊的(de)(de)全部(bu)數據(ju)(ju)(ju)(ju),直接使用預(yu)計(ji)(ji)(ji)算結(jie)果,完全不需(xu)要(yao)讀取數據(ju)(ju)(ju)(ju)塊的(de)(de)內容。由于(yu)預(yu)計(ji)(ji)(ji)算數據(ju)(ju)(ju)(ju)量(liang)遠小(xiao)于(yu)磁盤上存(cun)儲的(de)(de)數據(ju)(ju)(ju)(ju)塊數據(ju)(ju)(ju)(ju)的(de)(de)大小(xiao),對于(yu)磁盤 I/O 為(wei)瓶頸的(de)(de)查詢(xun)處(chu)(chu)理,使用預(yu)計(ji)(ji)(ji)算結(jie)果可以極大地減小(xiao)讀取 I/O 壓(ya)力,加速查詢(xun)處(chu)(chu)理的(de)(de)流程(cheng)。預(yu)計(ji)(ji)(ji)算機制與 Postgre SQL 的(de)(de)索引 BRIN(block range index)有異(yi)曲同工之(zhi)妙。

