• 写给 MySQL 开发者的 TDengine 入门指南


    MySQL 是中国开发者最熟悉的开源数据库产品,在很多开发者心中 MySQL 就是关系数据库的代名词。开发者们对 MySQL 数据库的的特性已经非常熟悉了。你也可以利用 CSDN 的 MySQL 技能树检验一下自己的掌握情况。

    TDengine (https://github.com/taosdata/TDengine)是完全面向处理时序数据而设计的数据库,是数据库领域的“新物种”,也就是所谓的时序数据库Time Series Database,简称 TSDB)。TDengine 在创立伊始,就坚定地走兼容 SQL 的路线,这极大地降低了数据库用户的使用门槛,但是另一方面,时序数据和关系数据库的处理方式还是有些区别,所以熟悉 MySQL 的用户在入手 TDengine 的时候会有一些混淆。所以,我们专门撰写了这篇文章,希望可以帮助广大熟悉 MySQL 数据库的开发者更快地上手 TDengine。

    但同时我们也要指出,TDengine 是专门为处理时序数据而设计的产品,并不适合存储非时序类型数据,在实际应用中,可以结合关系型数据库一起使用。

    注:本文以最新的 TDengine 3.0.1.4 版本为例。

    时序数据建模

    用 MySQL 关系型数据库给时序数据建模

    为了方便大家理解,我们先用大家熟悉的关系型数据库进行建模。建模的场景是我们要采集一万个电表的数据,每个电表有自己的设备 ID(device_id),有所在的位置(location),电表有不同型号(group_id)。每次采集我们要记录当时的时间戳(ts)、电表的电流(current)、电压(voltage)、相位(phase)三个数据。

    创建数据库的 SQL 语句:

    CREATE SCHEMA `test`;

    表 meters

    字段数据类型长度(字节)索引说明
    device_idVARCHAR8PK,FK_meters_devices设备 id,外键关联 devices.device_id
    tsTIMESTAMP(3)6PK时间戳,每个设备产生的时序记录时间戳唯一,所以 device_id 和 ts 创建联合主键。指定精度到毫秒。
    currentFLOAT4电流值
    voltageINT4电压值
    phaseFLOAT4相位值

     创建表 meters 的 SQL 语句:

    1. CREATE TABLE `test`.`meters` (
    2. `device_id` VARCHAR(8) NOT NULL,
    3. `ts` TIMESTAMP(3) NOT NULL,
    4. `current` FLOAT NULL,
    5. `voltage` INT NULL,
    6. `phase` FLOAT NULL,
    7. PRIMARY KEY (`device_id`, `ts`),
    8. CONSTRAINT `FK_meters_devices`
    9. FOREIGN KEY (`device_id`)
    10. REFERENCES `test`.`devices` (`device_id`));

    表 devices

    字段数据类型长度(字节)索引说明
    device_idVARCHAR8PK设备 id
    locationVARCHAR24IDX_locationlocation 是设备的属性
    group_idINT4IDX_group_idgroup_id 是设备的属性

    创建表 devices 的 SQL 语句:

    1. CREATE TABLE `test`.`devices` (
    2. `device_id` VARCHAR(8) NOT NULL,
    3. `location` VARCHAR(24) NOT NULL,
    4. `group_id` INT NOT NULL,
    5. PRIMARY KEY (`device_id`),
    6. INDEX `IDX_location` (`location` ASC),
    7. INDEX `IDX_group_id` (`group_id` ASC));

    把 MySQL 建模转换成 TDengine 建模

    我们来对比看看 TDengine 的建模和 MySQL 有什么不同。让我们先引入 TDengine 的两个概念:

    1、一个设备采集点一张表

    根据这一设计,device_id 就是子表名称。location 和 group_id 我们作为子表的 TAG。主键:在 TDengine 中,表的第一个字段必须是 TIMESTAMP 类型,并且会被自动设置为主键。

    2、超级表与子表

    在一个设备采集点一张表的设计理念下,对应设备的数量,会出现成千上万乃至上亿张表,TDengine 为此又引入了“超级表”和“子表”两个概念,它们有如下几个主要特征:

    • 超级表是子表的模板,定义了子表的数据结构,所有子表都是由超级表“派生”出来,修改超级表结构就是修改所有子表结构;
    • 基于超级表可以轻松进行分组聚合查询,查出每个子表的聚合计算后的数据,如:查询每个电表的总用电量;
    • 标签(TAG)可以理解为定义在超级表中的字段,每一个子表只有一组标签值,代表一个采集点的静态数据且为内存存储。在 SELECT 语句查询的时候,标签(TAG)值可以像普通字段一样出现在查询结果中。

    更多 TDengine 超级表文档请参考:超级表 | TDengine 文档 | 涛思数据

    现在让我们用 TDengine 进行建模,创建数据库的 SQL 语句:

    CREATE DATABASE `test`;

    然后创建一张超级表:

    表 meters

    字段数据类型长度(字节)索引说明
    tsTIMESTAMP8PK时间戳,每个设备产生的时序记录时间戳唯一,所以 device_id 和 ts 创建联合主键
    currentFLOAT4电流值
    voltageINT4电压值
    phaseFLOAT4相位值
    locationVARCHAR24标签(TAG)
    group_idINT4标签(TAG)

    创建表 metrics 的 SQL 语句:

    1. CREATE STABLE `test`.`meters` (
    2. `ts` TIMESTAMP,
    3. `current` FLOAT,
    4. `voltage` INT,
    5. `phase` FLOAT)
    6. TAGS (
    7. `group_id` INT,
    8. `location` VARCHAR(24));

    关于数据类型的对比

    数据类型MySQLTDengine
    TIMESTAMP默认精度为秒,可以支持到微秒。默认精度为毫秒。可支持微秒和纳秒,需要在创建数据库时指定。
    VARCHAR存储可变长度的多字节字符串,按字符存储。存储可变长度的单字节字符串,只用于处理 ASCII 可见字符。VARCHAR 是 BINARY 的别称。
    CHAR存储固定长度的多字节字符串,按字符存储。不存在。
    NCHAR存储固定长度的多字节字符串,按字符存储。同 CHAR。存储可变长度的多字节字符串,如中文字符。等同于 MySQL 的 CHAR 和默认字符集(utf8)的 VARCHAR。
    VARBINARY存储可变长度的二进制字符串,按字节存储。当前版本不存在,后续版本提供。
    BINARY存储固定长度的二进制字符串,按字节存储。存储可变长度的单字节字符串,只用于处理 ASCII 可见字符。
    JSON存储 JSON 数据结构。只有标签(TAG)可以用 JSON 类型。

    TDengine 数据类型文档请参考:数据类型 | TDengine 文档 | 涛思数据

    关键字和保留词

    MySQL 和 TDengine 的关键字/保留词略有不同,所以有些情况下创建表名、字段名时候,需要注意加上反引号 “ 进行转义。举例:

    • TTL 在 TDengine 中是关键字,但在 MySQL 中不是。
    • CURRENT 在 MySQL 中是关键字,但在 TDengine 中不是。

    数据插入与更新

    下面让我们来体验下数据处理的真实例子:

    插入采集数据

    MySQL

    插入设备数据

    按照上面的建模,MySQL 插入数据之前,需要先准备好设备数据,下面我们准备几条:

    1. INSERT INTO `test`.`devices` VALUES ('d1001', 'California.SanFrancisco', 2);
    2. INSERT INTO `test`.`devices` VALUES ('d1002', 'California.SanFrancisco', 3);
    3. INSERT INTO `test`.`devices` VALUES ('d1003', 'California.LosAngeles', 3);

    插入采集数据

    1. INSERT INTO `test`.`meters` VALUES ('d1001', '2018-09-08 17:51:04.777', 10.3, 219, 0.31);
    2. INSERT INTO `test`.`meters` VALUES ('d1002', '2018-09-08 17:51:04.777', 10.2, 220, 0.23);
    3. INSERT INTO `test`.`meters` VALUES ('d1003', '2018-09-08 17:51:04.777', 11.5, 221, 0.35);

    TDengine

    创建子表

    因为 TDengine 的设备属性通过标签(TAG)的方式表达,所以在创建子表的时候来定义设备的属性(对应 MySQL 的插入设备数据)。让我们先来创建子表:

    1. CREATE TABLE `test`.`d1001` USING `test`.`meters` (`group_id`, `location`) TAGS (2, "California.SanFrancisco");
    2. CREATE TABLE `test`.`d1002` USING `test`.`meters` (`group_id`, `location`) TAGS (3, "California.SanFrancisco");
    3. CREATE TABLE `test`.`d1003` USING `test`.`meters` (`group_id`, `location`) TAGS (3, "California.LosAngeles");

    以上语句的语义是通过使用(USING)超级表`test`.`meters`,来创建对应标签(TAGS)的子表。

    插入采集数据

    1. INSERT INTO `test`.`d1001` VALUES ('2018-09-08 17:51:04.777', 10.3, 219, 0.31);
    2. INSERT INTO `test`.`d1001` VALUES ('2018-09-08 17:51:04.777', 10.2, 220, 0.23);
    3. INSERT INTO `test`.`d1001` VALUES ('2018-09-08 17:51:04.777', 11.5, 221, 0.35);

    插入采集数据时自动创建子表

    TDengine 还有更便捷的方式,可以让创建子表和插入数据在同一条语句中实现:

    INSERT INTO `test`.`d1001` USING `test`.`meters` TAGS ('California.SanFrancisco', 2) VALUES ('2018-09-08 17:51:04.777', 10.3, 219, 0.31);

    关于写入数据的详细文档,请参考:数据写入 | TDengine 文档 | 涛思数据

    更新采集数据

    MySQL

    我们先来看看 MySQL 如何更新数据:

    更新采集数据:

    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 语句,但是 TDengine 也支持更新。在 TDengine 中,INSERT 时间戳相同的数据,会更新原有记录:

    INSERT INTO `test`.`d1001` VALUES ('2018-09-08 17:51:04.777', 10.4, 225, 0.35);

    注意:TDengine 2.x 版本需要在创建数据库时指定 UPDATE 参数,3.x 版本不需要。

    更新设备属性

    MySQL

    我们先来看看 MySQL 建模下如何更新设备属性:

    UPDATE `test`.`devices` SET `location` = 'California.LosAngeles', `group_id` = 3 WHERE `device_id` = 'd1001';

    TDengine

    如前文所述,TDengine 的设备属性存在于标签(TAG)之中,修改设备属性就是修改标签,所以要用修改标签的语句:

    1. ALTER TABLE `test`.`d1001` SET TAG `location` = 'California.LosAngeles';
    2. ALTER TABLE `test`.`d1001` SET TAG `group_id` = 5;

    注:标签只可单个修改。

    工具与可视化

    GUI 工具

    MySQL 官方从 5.0 版本开始提供了 MySQL Workbench 这个图形管理工具,目前 TDengine 还未提供官方的 GUI 管理工具,但是因为 TDengine 支持 JDBC 标准驱动,这就让 TDengine 可以通过 JDBC 驱动直接对接目前市面上大量的 SQL IDE 产品,比如 DBeaver、IDEA 等。TDengine 官方也提供了相关文档,供参考:

    如何通过开源数据库管理工具 DBeaver 连接 TDengine

     如何通过 IDEA 数据库管理工具连接 TDengine?

    此外,TDengine 企业版提供了 Taos Explorer,提供专门适配 TDengine 技术架构的完整 GUI 管理工具。如果你想使用 Taos Explorer,也可以直接联系官方企业咨询服务:技术支持 - TDengine | 涛思数据

    可视化

    TDengine 官方已经适配了 Grafana,在 Grafana 官方插件库里可以找到,详情请参考 TDengine 官方文档:Grafana | TDengine 文档 | 涛思数据

    导入导出工具

    MySQL 官方提供了 mysqldump 工具用来进行数据的导入和导出。同样的,TDengine 官方也提供了 taosdump 工具来进行相同的任务。详情请参考官方文档:数据导入 | TDengine 文档 | 涛思数据 和数据导出 | TDengine 文档 | 涛思数据

    容量与查询性能对比

    环境与数据准备

    设备环境:MacBook Pro 14 M1 8-core 16GB

    MySQL 版本:8.0.28

    TDengine 版本:3.0.1.4

    我们通过使用 taosBenchmark 工具(taosBenchmark | TDengine 文档 | 涛思数据),来随机生成一亿条采集数据,分布在一张超级表 meters 下的一万张子表中,并把相同的数据按上述建模模型导入进 MySQL,确保最后的比对结果一致。

    存储空间对比

    针对上述数据,MySQL 实际存储空间为 4,931 MB,TDengine 存储空间为 493 MB。

     

    查询性能对比

    典型查询一(COUNT)

    SELECT COUNT(*) FROM `test`.`meters`;

     MySQL

    TDengine

     

    总结:TDengine 查询性能是 MySQL 的 50 倍

    典型查询二(平均值,最大值、最小值)

    1. SELECT AVG(voltage) FROM `test`.`meters`;
    2. SELECT MAX(voltage) FROM `test`.`meters`;
    3. SELECT MIN(voltage) FROM `test`.`meters`;

     注:经过测试,MySQL 和 TDengine 对 AVG()、MAX()、MIN() 函数的查询时间均类似,所以不再额外展示。

    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

    典型查询四(分组查询)

    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 在支持标准 SQL 的基础之上,还提供了一系列满足时序业务场景需求的特色查询语法,这些语法能够为时序场景的应用的开发带来极大的便利。

    时间窗口切分查询

    TDengine 支持按时间窗口切分方式进行聚合结果查询,比如需查询每隔 1 秒钟的电流平均值。举例:

    SELECT _wstart, AVG(current) FROM `test`.`d1001` INTERVAL(1s);

    状态窗口切分查询

    使用整数(布尔值)或字符串来标识产生记录时候设备的状态量。产生的记录如果具有相同的状态量数值则归属于同一个状态窗口,数值改变后该窗口关闭。举例: 

    数据保留策略

    经过长时间累积大量数据以后,历史数据往往需要做归档或删除处理。MySQL 等关系型数据库只能通过执行计划任务调用 DELETE 语句根据时间条件删除数据。而 TDengine 天生就对数据保留策略提供了支持,一共有三种办法来灵活地处理:

    1. 创建数据库时,设定 KEEP 参数,比如 CREATE DATABASE test KEEP 100d; 表示数据库中的数据在保存 100 天后会被自动删除;
    2. 创建表时,设定 TTL 参数,单位为天,比如 CREATE TABLE meters ... TTL 50; 表示 50 天之后,表会被系统自动删除;
    3. 冷热数据分级存储。在 TDengine 企业版中,支持把数据按照时间维度分别存储于不同的文件句柄,可以对应到不同的存储介质,比如将热数据存储于 SSD 磁盘,将冷数据存储到 S3 存储中。

    替代 MySQL 案例分享

    事实证明,在时序数据场景下,无论是在存储空间、写入速度还是查询性能等各方面,TDengine 都存在数量级优势。最后,提供一些使用新一代时序数据库 TDengine 替代传统关系性数据库 MySQL 的典型案例供参考:

    欢迎添加小T(VX:TDengine),加入物联网技术讨论群,第一时间了解TDengine 官方信息,与关注前沿技术的同学们共同探讨新技术、新玩法。


    想了解更多 TDengine Database的具体细节,欢迎大家在GitHub上查看相关源代码。

  • 相关阅读:
    jar 命令启动java 指定配置文件路径 jar如何启动
    String类
    从 IPv4 向 IPv6 的迁移
    先有网络模型的使用及修改
    阿里一面: Spring 有哪些扩展点?
    postman使用pre-request script
    Java项目_家庭记账(简易版)
    PaddleOCR 更换模型
    Redis五大基本数据类型
    Linux内存不够了?看看如何开启虚拟内存增加内存使用量
  • 原文地址:https://blog.csdn.net/taos_data/article/details/127653512