– 写给关系数据库开发者的时序数据库入门指南
MySQL 是中国开发者最熟悉的开源数据库产品,在很多开发者心中 MySQL 就是关系数据库的代名词。开发者们对 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 语句:
表 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 语句:
表 devices
字段 | 数据类型 | 长度(字节) | 索引 | 说明 |
device_id | VARCHAR | 8 | PK | 设备 id |
location | VARCHAR | 24 | IDX_location | location 是设备的属性 |
group_id | INT | 4 | IDX_group_id | group_id 是设备的属性 |
创建表 devices 的 SQL 语句:
把 MySQL 建模转换成 TDengine 建模
我们来对比看看 TDengine 的建模和 MySQL 有什么不同。让我们先引入 TDengine 的两个概念:
1、一个设备采集点一张表
根据这一设计,device_id 就是子表名称。location 和 group_id 我们作为子表的 TAG。主键:在 TDengine 中,表的第一个字段必须是 TIMESTAMP 类型,并且会被自动设置为主键。
2、超级表与子表
在一个设备采集点一张表的设计理念下,对应设备的数量,会出现成千上万乃至上亿张表,TDengine 为此又引入了“超级表”和“子表”两个概念,它们有如下几个主要特征:
- 超级表是子表的模板,定义了子表的数据结构,所有子表都是由超级表“派生”出来,修改超级表结构就是修改所有子表结构;
- 基于超级表可以轻松进行分组聚合查询,查出每个子表的聚合计算后的数据,如:查询每个电表的总用电量;
- 标签(TAG)可以理解为定义在超级表中的字段,每一个子表只有一组标签值,代表一个采集点的静态数据且为内存存储。在 SELECT 语句查询的时候,标签(TAG)值可以像普通字段一样出现在查询结果中。
更多 TDengine 超级表文档请参考:https://docs.taosdata.com/taos-sql/stable/。
现在让我们用 TDengine 进行建模,创建数据库的 SQL 语句:
-
CREATE DATABASE `test`;
然后创建一张超级表:
表 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 语句:
关于数据类型的对比
数据类型 | MySQL | TDengine |
TIMESTAMP | 默认精度为秒,可以支持到微秒。 | 默认精度为毫秒。可支持微秒和纳秒,需要在创建数据库时指定。 |
VARCHAR | 存储可变长度的多字节字符串,按字符存储。 | 存储可变长度的单字节字符串,只用于处理 ASCII 可见字符。VARCHAR 是 BINARY 的别称。 |
CHAR | 存储固定长度的多字节字符串,按字符存储。 | 不存在。 |
NCHAR | 存储固定长度的多字节字符串,按字符存储。同 CHAR。 | 存储可变长度的多字节字符串,如中文字符。等同于 MySQL 的 CHAR 和默认字符集(utf8)的 VARCHAR。 |
VARBINARY | 存储可变长度的二进制字符串,按字节存储。 | 当前版本不存在,后续版本提供。 |
BINARY | 存储固定长度的二进制字符串,按字节存储。 | 存储可变长度的单字节字符串,只用于处理 ASCII 可见字符。 |
JSON | 存储 JSON 数据结构。 | 只有标签(TAG)可以用 JSON 类型。 |
TDengine 数据类型文档请参考:https://docs.taosdata.com/taos-sql/data-type/。
关键字和保留词
MySQL 和 TDengine 的关键字/保留词略有不同,所以有些情况下创建表名、字段名时候,需要注意加上反引号 “ 进行转义。举例:
-
TTL
在 TDengine 中是关键字,但在 MySQL 中不是。 -
CURRENT
在 MySQL 中是关键字,但在 TDengine 中不是。
数据插入与更新
下面让我们来体验下数据处理的真实例子:
插入采集数据
MySQL
插入设备数据
按照上面的建模,MySQL 插入数据之前,需要先准备好设备数据,下面我们准备几条:
插入采集数据
TDengine
创建子表
因为 TDengine 的设备属性通过标签(TAG)的方式表达,所以在创建子表的时候来定义设备的属性(对应 MySQL 的插入设备数据)。让我们先来创建子表:
以上语句的语义是通过使用(USING)超级表`test`.`meters`,来创建对应标签(TAGS)的子表。
插入采集数据
插入采集数据时自动创建子表
TDengine 还有更便捷的方式,可以让创建子表和插入数据在同一条语句中实现:
关于写入数据的详细文档,请参考:https://docs.taosdata.com/taos-sql/insert/。
更新采集数据
MySQL
我们先来看看 MySQL 如何更新数据:
更新采集数据:
TDengine
TDengine 中没有 UPDATE 语句,但是 TDengine 也支持更新。在 TDengine 中,INSERT 时间戳相同的数据,会更新原有记录:
注意:TDengine 2.x 版本需要在创建数据库时指定 UPDATE 参数,3.x 版本不需要。
更新设备属性
MySQL
我们先来看看 MySQL 建模下如何更新设备属性:
TDengine
如前文所述,TDengine 的设备属性存在于标签(TAG)之中,修改设备属性就是修改标签,所以要用修改标签的语句:
注:标签只可单个修改。
工具与可视化
GUI 工具
MySQL 官方从 5.0 版本开始提供了 MySQL Workbench 这个图形管理工具,目前 TDengine 还未提供官方的 GUI 管理工具,但是因为 TDengine 支持 JDBC 标准驱动,这就让 TDengine 可以通过 JDBC 驱动直接对接目前市面上大量的 SQL IDE 产品,比如 DBeaver、IDEA 等。TDengine 官方也提供了相关文档,供参考:
如何通过开源数据库管理工具 DBeaver 连接 TDengine