在用(yong) TDengine 進行(xing)數據(ju)建模(mo)之(zhi)前(qian),我們(men)需要(yao)回答兩個關鍵問題:建模(mo)的(de)目標(biao)用(yong)戶是(shi)誰?他們(men)的(de)具(ju)體需求(qiu)是(shi)什么(me)?在一個典型的(de)時序數據(ju)管理方案中,數據(ju)采集和數據(ju)應(ying)用(yong)是(shi)兩個主要(yao)環(huan)節。如下圖所示:

對于數(shu)(shu)據(ju)采(cai)集(ji)工程師而(er)言,他們的(de)主要(yao)需求是簡單、高效(xiao)地收集(ji)數(shu)(shu)據(ju)。為此,可以考慮創(chuang)建(jian)(jian)一個貼源(yuan)層(ceng),該層(ceng)的(de)數(shu)(shu)據(ju)模型建(jian)(jian)議與數(shu)(shu)據(ju)源(yuan)完全一致。這種方式能夠大(da)(da)大(da)(da)簡化(hua)數(shu)(shu)據(ju)采(cai)集(ji)的(de)過程,使得數(shu)(shu)據(ju)采(cai)集(ji)工作更加輕松和直觀。參見上(shang)圖貼源(yuan)層(ceng)。
另一方(fang)面,對(dui)于數據(ju)應用(yong)開發工程師(shi)來說(shuo),他們需(xu)(xu)要(yao)處理不(bu)同(tong)業務(wu)部門的需(xu)(xu)求。這(zhe)些工程師(shi)希(xi)望數據(ju)能夠(gou)按照業務(wu)主(zhu)題分(fen)類(lei),并能夠(gou)按照預(yu)期的訪(fang)(fang)問方(fang)式來建模(mo)。為了(le)滿足這(zhe)一需(xu)(xu)求,需(xu)(xu)要(yao)在數據(ju)模(mo)型中考慮訪(fang)(fang)問層的設計,使得數據(ju)應用(yong)更加便捷。參見上圖訪(fang)(fang)問層。
顯然,在數據采集和數據應用之間存在著巨大的鴻溝,那我們如何才能完成時序數據從采集到應用的轉換?這就需要引入數據分層的思想。具體來說(shuo),可以在貼源層(ceng)和訪問層(ceng)之間(jian)增加一個整(zheng)(zheng)合(he)層(ceng),用于完成時序數據的(de)時間(jian)戳對齊、關聯整(zheng)(zheng)合(he)、數據匯聚和數據轉換等功能。這一整(zheng)(zheng)合(he)層(ceng)的(de)引(yin)入,能夠實現從數據采集到數據應用的(de)無縫轉換,滿足不同階(jie)段的(de)需(xu)求。
本篇文章將從時序(xu)數據采集和應用面臨的挑戰及(ji)需求出發,為大(da)家(jia)分析 TDengine 數據建模的原理(li)與(yu)方法,并通(tong)過實際案例(li)幫助讀者更(geng)好地理(li)解(jie)和應用這些概念(nian)。
時序數據的采集和應用開發
采集面臨的挑戰
對于時序數據采集工程師來說,面臨的挑戰主要是數據源的格式、數據傳輸方式、采集入庫后的數據模型,在貼(tie)源(yuan)層(ceng)與采集(ji)源(yuan)的(de)(de)(de)模(mo)(mo)型按照 1:1 創建方式下(xia),數據源(yuan)的(de)(de)(de)格(ge)(ge)式和入庫后的(de)(de)(de)數據模(mo)(mo)型的(de)(de)(de)格(ge)(ge)式對(dui)齊了(le),也就不需要擔(dan)心格(ge)(ge)式不同(tong)導致的(de)(de)(de)問(wen)題了(le)。
最常見的數(shu)據傳輸方式(shi)有三種(zhong)情況:

- 單表多列模型:對于數據源來說,如果一個數據采集設備的所有測點,在數據上傳時都是一起上傳,那么,可以建立單表多列模型的超級表。也就是說,創建一個超級表,該表中包含該采集設備的所有測點指標。
- 多表多列模型:對于數據源來說,如果一數據采集設備包含多個測點,但這些測點的采集頻次并不相同,舉例來說:該設備總共采集 120 個測量指標,其中前 40 個測點的采集周期是秒級、中間 40 個測點采集周期是 1 分鐘級、最后 40 個測點的采集周期是 5 分鐘級,那么,我們需要分別創建三個超級表,第一個超級表包含前 40 個測量值,第二個超級表包含中間 40 個測量值,第三個超級表包含最后的 40 個測量值。這就是多表多列模型,也就是說,創建多個超級表,每個超級表也包含多個列。
- 多表單列模型:如果數據源是每個測點單獨上報,并且每個測點的采集時間戳并不相同,對于這種情況,需要按照數據類型分類,采用多表單列模型。也就是說,每個超級表中只包含一個數據類型,比如 double、int、varchar 等等,每種數據類型一個超級表。子表按照數據類型+設備類型來創建,讓子表的數量保持在合理的規模,這樣能夠利用多個 Vnode 來保障性能。
應用開發面臨的挑戰
對于時序數(shu)(shu)據應(ying)用開發(fa)的工(gong)程師來說,時序數(shu)(shu)據能否按照業(ye)務(wu)主(zhu)題劃分、能否支持(chi)實時查詢(xun)和批量查詢(xun)等業(ye)務(wu)場景,這(zhe)些因(yin)素都十分關鍵。如下圖所(suo)示(shi):

- 按照主題劃分:以卷煙廠為例,卷煙廠包括多個車間如制絲和卷包,對應的業務應用往往有制絲集控、卷包數采等等。如果我們將這些業務應用按照業務主題劃分,分別進行數據建模就能夠極大地方便應用開發。各個業務主題共享數據整合層的數據,整合層的數據來源于貼源層,是經過時間戳對齊、數據關聯整合、數據匯聚的。
- 支持實時查詢:對于時序數據來說,實時監控是非常典型的業務場景,TDengine 提供了緩存、流計算和數據訂閱三種方式,供應用層實時訪問。
- 緩存:對于實時監控場景,TDengine 提供了緩存功能,在創建數據庫時,可以通過設置 CACHEMODEL 參數,讓 TDengine 在內存中緩存各個子表的最新數據。對于業務應用來說,可以通過 last_row/last,從緩存中實時讀取設備的最新狀態。
- 流計算:TDengine 的流式計算引擎提供了實時處理寫入數據流的能力,使用 SQL 定義實時數據流的轉換規則,當數據被寫入流的源表后,數據會被以指定的方式自動處理,并根據指定的觸發模式向目標表推送計算結果。它提供了替代復雜流處理系統的輕量級解決方案,并且,能夠在高吞吐的數據寫入的情況下,將流計算的延遲控制在毫秒級。
- 數據訂閱:除了上述的流計算,TDengine 還提供了類似 Kafka 的數據訂閱功能,幫助應用實時獲取寫入 TDengine 的數據,或者以事件到達順序處理數據。TDengine 的 topic 有三種,可以是數據庫、超級表、或者一個 SELECT 語句。這種方式提供了更大的靈活性,數據的顆粒度可以隨時調整,而且數據的過濾與預處理交給 TDengine,有效地減少傳輸的數據量,并且,降低了應用開發的復雜度。
- 支持批量查詢:對于批量時序數據查詢場景,TDengine 提供了 SQL 接口給上層應用查詢批量數據使用。也提供了諸多時序數據窗口函數,包括計數窗口(count window)、時間窗口(time window)、狀態窗口(status window)、會話窗口(session window)、事件窗口(event window)等多種窗口。
- taosx:除了通過 SQL 查詢之外,對于數據量比較大,需要通過文件或數據庫接口同步數據的場景,還可以考慮使用 taosx 來同步數據。
數據建模原理和方法
數據建模原理
TDengine 數據建模的核心原理只有一個:讓查詢直接定位到數據塊。首先,我們來觀察一下 TDengine 是如何將規模很大的數據切分成很多個數據塊的。TDengine 分別從采集點維度和時間戳維度對大規模數據進行分片(Sharding)和分區(Partition),如下圖所示。

對于數據建模來說,我們要充分利用分片(Sharding)和分區(Partition)這兩大維度對數(shu)(shu)據(ju)進(jin)行切分,確保在查(cha)(cha)詢時,我們(men)的(de)(de)(de)過(guo)(guo)濾(lv)條(tiao)件(Where 子句)能夠直(zhi)接定位某(mou)個、或者某(mou)幾個數(shu)(shu)據(ju)塊(kuai),數(shu)(shu)據(ju)塊(kuai)的(de)(de)(de)個數(shu)(shu)越(yue)少(shao)越(yue)好(hao)。定位到的(de)(de)(de)數(shu)(shu)據(ju)塊(kuai)越(yue)少(shao),說明過(guo)(guo)濾(lv)的(de)(de)(de)越(yue)高(gao)效,所需要的(de)(de)(de)磁盤IO帶寬(kuan)越(yue)小,查(cha)(cha)詢速度也越(yue)快(kuai)。
數據建模基本概念
請參見:。
智能電表(biao)是典型的(de)時(shi)序數據場景。假設每個(ge)智能電表(biao)采集(ji)電流、電壓、相(xiang)位三個(ge)量,有多(duo)個(ge)智能電表(biao),每個(ge)電表(biao)有位置 Location 和 type 的(de)靜態屬性。其采集(ji)的(de)數據類似如下的(de)表(biao)格:

每一條記錄都有設備 ID、時間戳、采集的物理量(如上表中的 current、voltage 和 phase)以及每個設備相關的靜態標簽(location 和 type)。
- 數據采集點(Data Collection Point):數據采集點是指按照預設時間周期或受事件觸發采集物理量的硬件或軟件。智能電表示例中的 d1001、d1002、d1003、d1004 等就是數據采集點。為充分利用其數據的時序性和其他數據特點,TDengine 采取一個數據采集點一張表的策略,按照此策略,上述 d1001、d1002、d1003、d1004 分別建表。
- 標簽(Label/Tag):標簽是指傳感器、設備或其他類型采集點的靜態屬性,不隨時間變化。比如設備型號、顏色、設備的所在地等。
- 采集量(Metric):采集量是指傳感器、設備或其他類型采集點采集的物理量。比如電流、電壓、溫度、壓力、GPS 位置等,是隨時間變化的。數據類型可以是整型、浮點型、布爾型,也可是字符串。
創建超級表
為(wei)智(zhi)能電(dian)表(biao)這(zhe)個設備類(lei)(lei)型建立(li)一個超級表(biao),采集量(liang)有電(dian)流(liu)、電(dian)壓和(he)(he)相位,標簽有位置和(he)(he)類(lei)(lei)型。
create table smeter (ts timestamp, current float, voltage int, phase float)
tags (loc binary(20), type int);
創建子表
用(yong) smeter 做模板,為 6 個(ge)智能電表創建 6 張表,地理位置標簽為北京(jing)朝陽、海淀、上海浦東等。
create table t1 using smeter tags(‘BJ.chaoyang’, 1);
create table t2 using smeter tags(‘BJ.haidian’, 2);
create table t3 using smeter tags(‘BJ.daxing’, 1);
create table t4 using smeter tags(‘BJ.chaoyang’, 2);
create table t5 using smeter tags(‘SH.pudong’,1);
create table t6 using smeter tags(‘SH.Hongqiao’, 1);
聚合查詢
查詢北京朝陽區所有智能電(dian)表的電(dian)壓平(ping)均值和電(dian)流最(zui)大值。
select avg(voltage), max(current) from smeters where loc= “BJ.chaoyang”
多維分析
查詢北京地區所(suo)有類(lei)型為 1 的(de)智能電(dian)(dian)表的(de)電(dian)(dian)壓平均值。
select avg(voltage) from smeters where type = 1 and loc like “BJ%”
TDengine 數據建模優勢
TDengine 數(shu)據(ju)建(jian)模的優勢(shi)包(bao)括:
- 超級表可以向普通表一樣查詢,但可以指定標簽的過濾條件
- 標簽可以多至 128 個,每個標簽代表一個維度
- 標簽可以事后增加、刪除、修改。這樣數據建模時,可以先不確定標簽或分析維度
- 每個標簽,可以是一樹狀結構,比如“北京·朝陽·望京”,這樣便于縮小搜索范圍
數據建模案例
以新能(neng)源(yuan)充電站建模(mo)場(chang)景為(wei)例(li)。
背景信息
該客戶(hu)管理(li)一些充電站:
- 充電站管理的充電樁大約有幾百個,每個充電樁每天可能多次進行充電
- 每次充電產生一個充電訂單,充電訂單每年大約有 1000 萬個,數據需要保留 2 年
- 需要監控充電過程中電壓、電流等信息,對于歷史訂單能夠回放充電過程
- 數據查詢方式:按照訂單查詢,期望能夠實時監控正在充電的訂單、以及能夠查詢歷史訂單充電過程
數據建模思考
常規思維,按照一個采集點一張表的原則,這里顯然會按照充電樁來創建子表。但是,這樣一來存在一個問題,我們按照訂單查詢時就無法直接定位到某個或者某幾個數據塊。按照充電樁來創建子表,數據的確會按照充電樁進行分片(Sharding),并且,分片后的數據也按照時間戳進行了分區(Partition),但(dan)是,業務查詢的(de)時候未指定時間戳范(fan)圍,而(er)是查詢指定的(de) order_id,所以,無法定位到(dao)具體的(de)數(shu)據(ju)塊,需要在(zai)分片中進行(xing)全量數(shu)據(ju)掃描,必然導致性能十分低(di)下。如下圖所示:

正確的建模思路
讓我們回顧 TDengine 數據建模原理:讓查詢直接定位到數據塊。業務(wu)希望(wang)按照訂單(dan)來(lai)查詢,那么,我們(men)直(zhi)接按照訂單(dan)來(lai)對(dui)數據進行分片(Sharding),也(ye)(ye)(ye)就(jiu)是說每個訂單(dan)創建一(yi)張子(zi)表(biao)。這(zhe)樣,按照訂單(dan)查詢時,我們(men)能夠(gou)直(zhi)接定位到該分片,這(zhe)樣的(de)好處是查詢的(de)性能非(fei)常好,也(ye)(ye)(ye)非(fei)常方便,但也(ye)(ye)(ye)存在兩個問(wen)題:
- 每年新建 1000 萬張子表,訂單數據保留 2 年,預計就有 2000 萬張子表,規模會不會太大?
- 數據保留周期是 2 年,對于超過 2 年的子表,是否能夠自動刪除呢?
幸運的是(shi),對(dui)于(yu)時(shi)序數(shu)(shu)(shu)據領域(yu)常(chang)見的“高基數(shu)(shu)(shu)”問(wen)題,TDengine 已經(jing)很好地解決了,2000 萬張子表(biao)對(dui)于(yu)關系型(xing)數(shu)(shu)(shu)據庫來說,可能是(shi)天(tian)文數(shu)(shu)(shu)字,但是(shi),對(dui)于(yu) TDengine 來說就(jiu)是(shi)小菜一(yi)碟(die)。對(dui)于(yu)超過(guo) 2 年的子表(biao),TDengine 提供(gong)了 TTL(Time to Live),是(shi)用來指定表(biao)的生命周期(單(dan)位:天(tian))。如果創(chuang)建表(biao)時(shi)指定了這個參數(shu)(shu)(shu),當(dang)該表(biao)的存在時(shi)間(jian)超過(guo) TTL 指定的時(shi)間(jian)后,TDengine 將自動刪(shan)除該表(biao)。
-- 為訂單 801234567 創建 表 order_801234567,保留周期 732 天,到期自動 drop
create table order_801234567 using charge_order tags(801234567, 235) TTL 732;
實際數據建模
創建超級表
-- 充電訂單超級表,標簽值(訂單號,充電搶號)
create table charge_order (ts timestamp, ……)
tags (order_id, gun_id);
為單個訂(ding)單創建子表(假設訂(ding)單號:801234567),保留(liu) 2 年到期自動刪除(chu)
-- 為訂單 801234567 創建表 order_801234567,保留周期 732 天,到期自動 drop
create table order_801234567 using charge_order tags(801234567, 235) TTL 732;
按照充電訂單查詢
-- 按照充電訂單查詢
select * from charge_order where order_id = 801234567;
寫在最后
通過對時序數(shu)(shu)據(ju)(ju)采集和應用(yong)(yong)的(de)(de)挑戰及需求的(de)(de)分析(xi),本文深(shen)入探討(tao)了 TDengine 數(shu)(shu)據(ju)(ju)建(jian)模(mo)的(de)(de)原(yuan)理(li)與(yu)方(fang)法。我(wo)們不僅揭示了數(shu)(shu)據(ju)(ju)建(jian)模(mo)的(de)(de)核心(xin)概念(nian)和技術細節,還通過實(shi)際(ji)案(an)例展示了其在新能源場(chang)景下的(de)(de)應用(yong)(yong)效果。希望讀者能從中獲得啟發,在實(shi)際(ji)工(gong)作中靈活運用(yong)(yong) TDengine 數(shu)(shu)據(ju)(ju)建(jian)模(mo)的(de)(de)方(fang)法,提(ti)高時序數(shu)(shu)據(ju)(ju)管理(li)的(de)(de)效率與(yu)質量(liang)。


























