– 寫給關系數(shu)據庫(ku)開(kai)發者的時序數(shu)據庫(ku)入門指南
MySQL 是(shi)中國開(kai)(kai)發者最熟悉(xi)的開(kai)(kai)源數(shu)據(ju)庫(ku)產品,在很多開(kai)(kai)發者心中 MySQL 就(jiu)是(shi)關(guan)系數(shu)據(ju)庫(ku)的代名(ming)詞。開(kai)(kai)發者們對(dui) MySQL 數(shu)據(ju)庫(ku)的的特(te)性已經非常(chang)熟悉(xi)了。
TDengine ()是完全面向處理時序數據而設計的數據庫,是數據庫領域的“新物種”,也就是所謂的時序數據庫(Time Series Database,簡稱 TSDB)。TDengine 在(zai)創(chuang)立伊(yi)始,就堅定地走兼(jian)容 SQL 的(de)路(lu)線(xian),這極大地降低了數(shu)(shu)據(ju)庫用(yong)戶(hu)的(de)使用(yong)門檻,但(dan)是另(ling)一方面,時(shi)序(xu)數(shu)(shu)據(ju)和關系數(shu)(shu)據(ju)庫的(de)處(chu)理方式還(huan)是有(you)些區別,所(suo)(suo)以熟悉(xi) MySQL 的(de)用(yong)戶(hu)在(zai)入手 TDengine 的(de)時(shi)候會有(you)一些混淆。所(suo)(suo)以,我們(men)專門撰寫了這篇(pian)文章(zhang),希望可(ke)以幫助廣大熟悉(xi) MySQL 數(shu)(shu)據(ju)庫的(de)開發者更快地上手 TDengine。
但同時我們也要指出,TDengine 是專門為處理時序數據而設計的產品,并不適合存儲非時序類型數據,在實際應用中,可以結合關系型數據庫一起使用。
注(zhu):本(ben)文以最新的 TDengine 3.0.1.4 版本(ben)為例。
時序數據建模
用 MySQL 關系型數據庫給時序數據建模
為了方便大(da)家理解,我們先(xian)用(yong)大(da)家熟(shu)悉的(de)(de)關(guan)系型(xing)數(shu)據(ju)庫進行建模。建模的(de)(de)場(chang)景是(shi)我們要采集一(yi)萬個電(dian)(dian)表(biao)(biao)的(de)(de)數(shu)據(ju),每個電(dian)(dian)表(biao)(biao)有自(zi)己的(de)(de)設備(bei) ID(device_id),有所在的(de)(de)位(wei)置(location),電(dian)(dian)表(biao)(biao)有不(bu)同型(xing)號(group_id)。每次采集我們要記錄當(dang)時(shi)(shi)的(de)(de)時(shi)(shi)間戳(ts)、電(dian)(dian)表(biao)(biao)的(de)(de)電(dian)(dian)流(current)、電(dian)(dian)壓(voltage)、相(xiang)位(wei)(phase)三個數(shu)據(ju)。
創建數據庫(ku)的 SQL 語句:
CREATE SCHEMA `test`;
表 meters
| 字段 | 數據類型 | 長度(字節) | 索引 | 說明 |
| device_id | VARCHAR | 8 | PK,FK_meters_devices | 設備 id,外鍵關聯 devices.device_id |
| ts | TIMESTAMP(3) | 6 | PK | 時間戳,每個設備產生的時序記錄時間戳唯一,所以 device_id 和 ts 創建聯合主鍵。指定精度到毫秒。 |
| current | FLOAT | 4 | 電流值 | |
| voltage | INT | 4 | 電壓值 | |
| phase | FLOAT | 4 | 相位值 |
創建表 meters 的 SQL 語句:
CREATE TABLE `test`.`meters` (
`device_id` VARCHAR(8) NOT NULL,
`ts` TIMESTAMP(3) NOT NULL,
`current` FLOAT NULL,
`voltage` INT NULL,
`phase` FLOAT NULL,
PRIMARY KEY (`device_id`, `ts`),
CONSTRAINT `FK_meters_devices`
FOREIGN KEY (`device_id`)
REFERENCES `test`.`devices` (`device_id`));
表 devices
| 字段 | 數據類型 | 長度(字節) | 索引 | 說明 |
| device_id | VARCHAR | 8 | PK | 設備 id |
| location | VARCHAR | 24 | IDX_location | location 是設備的屬性 |
| group_id | INT | 4 | IDX_group_id | group_id 是設備的屬性 |
創建表 devices 的(de) SQL 語(yu)句(ju):
CREATE TABLE `test`.`devices` (
`device_id` VARCHAR(8) NOT NULL,
`location` VARCHAR(24) NOT NULL,
`group_id` INT NOT NULL,
PRIMARY KEY (`device_id`),
INDEX `IDX_location` (`location` ASC),
INDEX `IDX_group_id` (`group_id` ASC));
把 MySQL 建模轉換成 TDengine 建模
我(wo)們來(lai)對(dui)比看看 TDengine 的建模(mo)和 MySQL 有什么不同。讓我(wo)們先(xian)引(yin)入(ru) TDengine 的兩個概念(nian):
1、一個設備采集點一張表
根(gen)據這一(yi)設計,device_id 就是子表名稱。location 和 group_id 我們作為(wei)(wei)子表的(de)(de) TAG。主(zhu)鍵(jian):在 TDengine 中,表的(de)(de)第(di)一(yi)個(ge)字段必須是 TIMESTAMP 類型,并且會被自(zi)動(dong)設置為(wei)(wei)主(zhu)鍵(jian)。
2、超級表與子表
在一個設(she)備采集點一張(zhang)表(biao)的設(she)計理念(nian)下,對應設(she)備的數量,會出現(xian)成千上萬乃至上億張(zhang)表(biao),TDengine 為此又引入(ru)了“超級表(biao)”和“子表(biao)”兩個概念(nian),它(ta)們有如下幾(ji)個主要特征(zheng):
- 超級表是子表的模板,定義了子表的數據結構,所有子表都是由超級表“派生”出來,修改超級表結構就是修改所有子表結構;
- 基于超級表可以輕松進行分組聚合查詢,查出每個子表的聚合計算后的數據,如:查詢每個電表的總用電量;
- 標簽(TAG)可以理解為定義在超級表中的字段,每一個子表只有一組標簽值,代表一個采集點的靜態數據且為內存存儲。在 SELECT 語句查詢的時候,標簽(TAG)值可以像普通字段一樣出現在查詢結果中。
更多 TDengine 超級表(biao)文檔請(qing)參考(kao):。
現在讓我們用 TDengine 進行建模,創建數據庫(ku)的 SQL 語句:
CREATE DATABASE `test`;
然后創(chuang)建一(yi)張超級表:
表 meters
| 字段 | 數據類型 | 長度(字節) | 索引 | 說明 |
| ts | TIMESTAMP | 8 | PK | 時間戳,每個設備產生的時序記錄時間戳唯一,所以 device_id 和 ts 創建聯合主鍵 |
| current | FLOAT | 4 | 電流值 | |
| voltage | INT | 4 | 電壓值 | |
| phase | FLOAT | 4 | 相位值 | |
| location | VARCHAR | 24 | 標簽(TAG) | |
| group_id | INT | 4 | 標簽(TAG) |
創建表 metrics 的 SQL 語句:
CREATE STABLE `test`.`meters` (
`ts` TIMESTAMP,
`current` FLOAT,
`voltage` INT,
`phase` FLOAT)
TAGS (
`group_id` INT,
`location` VARCHAR(24));
關于數據類型的對比
| 數據類型 | MySQL | TDengine |
| TIMESTAMP | 默認精度為秒,可以支持到微秒。 | 默認精度為毫秒。可支持微秒和納秒,需要在創建數據庫時指定。 |
| VARCHAR | 存儲可變長度的多字節字符串,按字符存儲。 | 存儲可變長度的單字節字符串,只用于處理 ASCII 可見字符。VARCHAR 是 BINARY 的別稱。 |
| CHAR | 存儲固定長度的多字節字符串,按字符存儲。 | 不存在。 |
| NCHAR | 存儲固定長度的多字節字符串,按字符存儲。同 CHAR。 | 存儲可變長度的多字節字符串,如中文字符。等同于 MySQL 的 CHAR 和默認字符集(utf8)的 VARCHAR。 |
| VARBINARY | 存儲可變長度的二進制字符串,按字節存儲。 | 當前版本不存在,后續版本提供。 |
| BINARY | 存儲固定長度的二進制字符串,按字節存儲。 | 存儲可變長度的單字節字符串,只用于處理 ASCII 可見字符。 |
| JSON | 存儲 JSON 數據結構。 | 只有標簽(TAG)可以用 JSON 類型。 |
TDengine 數據類型(xing)文(wen)檔(dang)請(qing)參考:。
關鍵字和保留詞
MySQL 和 TDengine 的關鍵(jian)字/保留(liu)詞略有不同,所以有些(xie)情(qing)況(kuang)下(xia)創建表(biao)名(ming)、字段(duan)名(ming)時候,需要注意加上反引號 “ 進行轉義(yi)。舉例:
TTL在 TDengine 中是關鍵字,但在 MySQL 中不是。CURRENT在 MySQL 中是關鍵字,但在 TDengine 中不是。
數據插入與更新
下面讓(rang)我們來體驗(yan)下數據處理的真實例子:
插入采集數據
MySQL
插入設備數據
按照上面(mian)的建(jian)模,MySQL 插入數據之前(qian),需要先準備好設備數據,下面(mian)我們準備幾條:
INSERT INTO `test`.`devices` VALUES ('d1001', 'California.SanFrancisco', 2);
INSERT INTO `test`.`devices` VALUES ('d1002', 'California.SanFrancisco', 3);
INSERT INTO `test`.`devices` VALUES ('d1003', 'California.LosAngeles', 3);
插入采集數據
INSERT INTO `test`.`meters` VALUES ('d1001', '2018-09-08 17:51:04.777', 10.3, 219, 0.31);
INSERT INTO `test`.`meters` VALUES ('d1002', '2018-09-08 17:51:04.777', 10.2, 220, 0.23);
INSERT INTO `test`.`meters` VALUES ('d1003', '2018-09-08 17:51:04.777', 11.5, 221, 0.35);
TDengine
創建子表
因為 TDengine 的設備(bei)屬(shu)性(xing)(xing)通(tong)過標簽(qian)(TAG)的方式表(biao)達,所以在創(chuang)建(jian)子(zi)(zi)表(biao)的時候來定義設備(bei)的屬(shu)性(xing)(xing)(對(dui)應 MySQL 的插入設備(bei)數據(ju))。讓我們先(xian)來創(chuang)建(jian)子(zi)(zi)表(biao):
CREATE TABLE `test`.`d1001` USING `test`.`meters` (`group_id`, `location`) TAGS (2, "California.SanFrancisco");
CREATE TABLE `test`.`d1002` USING `test`.`meters` (`group_id`, `location`) TAGS (3, "California.SanFrancisco");
CREATE TABLE `test`.`d1003` USING `test`.`meters` (`group_id`, `location`) TAGS (3, "California.LosAngeles");
以(yi)上語句的語義是通過使用(USING)超(chao)級表`test`.`meters`,來創建對應(ying)標簽(qian)(TAGS)的子表。
插入采集數據
INSERT INTO `test`.`d1001` VALUES ('2018-09-08 17:51:04.777', 10.3, 219, 0.31);
INSERT INTO `test`.`d1001` VALUES ('2018-09-08 17:51:04.777', 10.2, 220, 0.23);
INSERT INTO `test`.`d1001` VALUES ('2018-09-08 17:51:04.777', 11.5, 221, 0.35);
插入采集數據時自動創建子表
TDengine 還有更便(bian)捷的方式(shi),可以讓創建子表和插(cha)入(ru)數據在(zai)同一(yi)條語句中實(shi)現:
INSERT INTO `test`.`d1001` USING `test`.`meters` TAGS ('California.SanFrancisco', 2) VALUES ('2018-09-08 17:51:04.777', 10.3, 219, 0.31);
關于寫入數據的(de)詳細文檔(dang),請參考:。
更新采集數據
MySQL
我們先來看(kan)看(kan) MySQL 如(ru)何(he)更新數據:
更新采集數據:
UPDATE `test`.`meters` SET `ts` = '2018-09-08 17:51:07', `current` = 10.4, `voltage` = 220, `phase` = 0.32 WHERE `device_id` = 'd1001' and `ts` = '2018-09-08 17:51:05';
TDengine
TDengine 中沒有 UPDATE 語句,但(dan)是 TDengine 也支(zhi)持更新(xin)(xin)。在 TDengine 中,INSERT 時間戳相同(tong)的數據,會更新(xin)(xin)原有記(ji)錄:
INSERT INTO `test`.`d1001` VALUES ('2018-09-08 17:51:04.777', 10.4, 225, 0.35);
注意:TDengine 2.x 版(ban)(ban)本需(xu)要(yao)在創建(jian)數據庫時指定 UPDATE 參數,3.x 版(ban)(ban)本不需(xu)要(yao)。
更新設備屬性
MySQL
我(wo)們先來看看 MySQL 建模(mo)下(xia)如(ru)何更新設備屬(shu)性:
UPDATE `test`.`devices` SET `location` = 'California.LosAngeles', `group_id` = 3 WHERE `device_id` = 'd1001';
TDengine
如前文所(suo)述,TDengine 的設備(bei)(bei)屬性存在于標簽(TAG)之中,修改設備(bei)(bei)屬性就是修改標簽,所(suo)以要用修改標簽的語句(ju):
ALTER TABLE `test`.`d1001` SET TAG `location` = 'California.LosAngeles';
ALTER TABLE `test`.`d1001` SET TAG `group_id` = 5;
注(zhu):標(biao)簽只可單個修(xiu)改。
工具與可視化
GUI 工具
MySQL 官方從 5.0 版本開始提(ti)供(gong)了 MySQL Workbench 這個圖形(xing)管理工具(ju),目(mu)前 TDengine 還(huan)未提(ti)供(gong)官方的 GUI 管理工具(ju),但(dan)是因為 TDengine 支持 JDBC 標準驅(qu)動,這就讓(rang) TDengine 可以通過 JDBC 驅(qu)動直接(jie)對接(jie)目(mu)前市面上大量(liang)的 SQL IDE 產品,比如 DBeaver、IDEA 等。TDengine 官方也提(ti)供(gong)了相關文檔,供(gong)參考:


此外,TDengine 企業版提供了 Taos Explorer,提供專門適配 TDengine 技術架構的完整 GUI 管理工具。如果你想使用 Taos Explorer,也可以直接聯系官方企業咨詢服務://yakult-sh.com.cn/support。
可視化
TDengine 官方已(yi)經(jing)適(shi)配了 Grafana,在 Grafana 官方插件庫里可以(yi)找到,詳情請參考 TDengine 官方文檔:。


導入導出工具
MySQL 官(guan)方提(ti)供(gong)了 mysqldump 工(gong)具用來進行(xing)(xing)數據的導入和導出。同樣的,TDengine 官(guan)方也提(ti)供(gong)了 taosdump 工(gong)具來進行(xing)(xing)相同的任務(wu)。詳情請參考官(guan)方文檔: 和。
容量與查詢性能對比
環境與數據準備
設備環境:MacBook Pro 14 M1 8-core 16GB
MySQL 版本:8.0.28
TDengine 版本:3.0.1.4
我們通過使用(yong) taosBenchmark 工(gong)具(),來隨機生(sheng)成一(yi)(yi)億條采集數(shu)據(ju),分布在一(yi)(yi)張(zhang)超級表 meters 下的(de)(de)一(yi)(yi)萬張(zhang)子表中,并把(ba)相(xiang)同的(de)(de)數(shu)據(ju)按上述(shu)建模(mo)模(mo)型導入進 MySQL,確保(bao)最(zui)后(hou)的(de)(de)比(bi)對結果一(yi)(yi)致。
存儲空間對比
針對上(shang)述數據,MySQL 實際存儲(chu)(chu)空間為 4,931 MB,TDengine 存儲(chu)(chu)空間為 493 MB。

查詢性能對比
典型查詢一(COUNT)
SELECT COUNT(*) FROM `test`.`meters`;
MySQL

TDengine

總結:TDengine 查詢性能是 MySQL 的 50 倍
典型查詢二(平均值,最大值、最小值)
SELECT AVG(voltage) FROM `test`.`meters`;
SELECT MAX(voltage) FROM `test`.`meters`;
SELECT MIN(voltage) FROM `test`.`meters`;
注:經過測試(shi),MySQL 和 TDengine 對 AVG()、MAX()、MIN() 函數的(de)查詢時間(jian)均類似,所以不再額(e)外展(zhan)示。
MySQL

TDengine

典型查詢三(條件查詢)
MySQL
SELECT COUNT(*) FROM `test`.`meters` m INNER JOIN `test`.`devices` d ON m.device_id = d.device_id WHERE d.location = "California.MountainView";

TDengine
SELECT COUNT(*) FROM `test`.`meters` WHERE location = "California.MountainView";

典型查詢四(分組查詢)
MySQL
SELECT AVG(m.voltage), d.location FROM `test`.`meters` m INNER JOIN `test`.`devices` d ON m.device_id = d.device_id GROUP BY d.location;

TDengine
SELECT AVG(voltage), location FROM `test`.`meters` GROUP BY location;

典型查詢五(時序業務)
MySQL
SELECT DATE_FORMAT(ts, '%Y%m%d-%H') AS date_format, AVG(voltage) FROM `test`.`meters` GROUP BY date_format;

TDengine
SELECT AVG(voltage) FROM `test`.`meters` INTERVAL(1h);

TDengine 的特色功能(時序數據處理)
TDengine 在支持標準(zhun) SQL 的基礎之上,還提供(gong)了一系列滿(man)足時序業(ye)務場景(jing)需求的特色查詢語法,這些語法能夠為(wei)時序場景(jing)的應用的開(kai)發帶來極大的便利。
時間窗口切分查詢
TDengine 支持按時(shi)間窗口(kou)切分方式進行聚合結果查詢,比如需查詢每隔 1 秒鐘的電(dian)流平均值。舉例:
SELECT _wstart, AVG(current) FROM `test`.`d1001` INTERVAL(1s);

狀態窗口切分查詢
使用(yong)整(zheng)數(布爾值)或(huo)字符(fu)串來標識產(chan)(chan)生記錄時候設備的狀(zhuang)態量(liang)(liang)。產(chan)(chan)生的記錄如果具(ju)有相同的狀(zhuang)態量(liang)(liang)數值則(ze)歸(gui)屬于同一(yi)個狀(zhuang)態窗(chuang)口,數值改變后該窗(chuang)口關閉。舉(ju)例:
SELECT COUNT(*), FIRST(ts), voltage FROM `test`.`meters` STATE_WINDOW(voltage) LIMIT 10;

數據保留策略
經過(guo)長時間累積大量數(shu)據以后,歷(li)史數(shu)據往往需要做歸(gui)檔(dang)或刪(shan)除處理。MySQL 等關(guan)系型數(shu)據庫只能通過(guo)執行計劃任務調用 DELETE 語句根據時間條(tiao)件(jian)刪(shan)除數(shu)據。而 TDengine 天生就對數(shu)據保(bao)留策略提供了(le)支持,一共有三種辦法(fa)來靈活地處理:
- 創建數據庫時,設定 KEEP 參數,比如
CREATE DATABASE test KEEP 100d;表示數據庫中的數據在保存 100 天后會被自動刪除; - 創建表時,設定 TTL 參數,單位為天,比如
CREATE TABLE meters ... TTL 50;表示 50 天之后,表會被系統自動刪除; - 冷熱數據分級存儲。在 TDengine 企業版中,支持把數據按照時間維度分別存儲于不同的文件句柄,可以對應到不同的存儲介質,比如將熱數據存儲于 SSD 磁盤,將冷數據存儲到 S3 存儲中。
替代 MySQL 案例分享
事實證明,在時序數(shu)據場景(jing)下,無論是在存(cun)儲空間、寫入速度(du)還是查(cha)詢性能等各(ge)方面(mian),TDengine 都存(cun)在數(shu)量級優勢。最后,提供一(yi)些使用新一(yi)代時序數(shu)據庫(ku) TDengine 替(ti)代傳(chuan)統(tong)關(guan)系性數(shu)據庫(ku) MySQL 的典型案(an)例供參考:
- 存儲空間降為 MySQL 的十分之一,TDengine 在貨拉拉數據庫監控場景的應用
- MySQL 無法滿足查詢性能?北明天時選擇 TDengine 實現熱網監控和能源分析
- 接手被 MySQL 卡死的數據,TDengine 在能源管理系統的應用
- MySQL 宕機?大數據驅動下的新零售,如何尋求存儲計算的最優解?



























