前言

上篇教程介绍了 Apache IoTDB 处理时序数据时,能够实现的部分具体功能和具体的操作命令,包括数据导入、基本查询、和聚合查询。

本篇将继续介绍 Apache IoTDB 可实现的其他功能和相关 SQL 语句命令,包括数据的写入、删除、导出、元数据操作和时区设置的注意事项。

1

数据写入、删除与导出

1.1

插入数据

物联网场景下,元件产生数据将自动写入,但有时候,如果过去的一些数据需要修改,可以使用 insert 语句插入修改后的值,覆盖原数据。

例如,我们可以向已有的时间序列 root.BHSFC.Q1.W002.speed 中插入单行数据,SQL 语句如下:

insert into root.BHSFC.Q1.W003(timestamp,speed) values(1657472400000,2)

我们可以使用查询语句查看是否插入成功:

select speed from root.BHSFC.Q1.W003

显示结果如下,可知已插入成功:

IoTDB> select speed from root.BHSFC.Q1.W003
+-----------------------------+------------------------+
|                         Time|root.BHSFC.Q1.W003.speed|
+-----------------------------+------------------------+
|2022-07-11T00:00:00.000+08:00|                     1.0|
|2022-07-11T01:00:00.000+08:00|                     2.0|
+-----------------------------+------------------------+
Total line number = 2

1.2

删除数据

1.2.1 SQL语句删除

1.2.1.1 删除整个时间序列

我们可以使用 SQL 语句删除单个时间序列,例如以下的 SQL 语句:

delete from root.BHSFC.Q1.W003.speed

我们可以验证一下,输入查询语句 select speed from root.BHSFC.Q1.W003 ,输出结果已经没有数据了,说明删除成功了:

```
IoTDB> select speed from root.BHSFC.Q1.W003
+----+------------------------------+
|Time|root.BHSFC.Q1.W003.speed|
+----+------------------------------+
+----+------------------------------+
Empty set.
```

1.2.1.2 删除时间范围内的数据

也可以与 where 语句结合删除某个时间片段,如删除 2022 年 1 月 14 日零点之后的数据:

delete from root.BHSFC.Q1.W003 where time<=2022-01-14T00:00:00

1.2.2 TTL 自动删除

TTL 是数据存活时间,将针对存储组进行运作。设置 TTL 后 Apache IoTDB 将自动删除此时间之前的数据,设置完毕即刻生效。

1.2.2.1 设置 TTL

设置 TTL 的 SQL 语句为:

set ttl to root.BHSFC.Q1.W003 3600000

这条语句表示在 root.BHSFCQ1.W003 设备中,最近一个小时的数据将会保存,旧数据会被删除。

1.2.2.2 取消 TTL

我们也可以取消这条时间序列的 TTL ,SQL 语句为:

unset ttl to root.BHSFC.Q1.W003

1.2.2.3 显示 TTL

我们还可以查询目前已设置的 TTL ,SQL 语句为:

show all ttl

输出的结果为:

+-------------+----+
|storage group| ttl|
+-------------+----+
|      root.ln|null|
|   root.BHSFC|null|
+-------------+----+
Total line number = 2

未设置 TTL 的存储组的 TTL 将显示为 null。

1.3

导出数据

我们可以使用 CSV 工具将数据导出。首先进入 tools 目录,然后输入 SQL 语句:

./export-csv.sh -h 127.0.0.1 -p 6667 -u root -pw root -td ./

之后会出现提示输入查询语句,我们按需输入 SQL 语句即可。例如我们要导出测试数据的平均值,则输入:

select AVG(*) from root.BHSFC.Q1.W002

结果显示如下:

ExportCsv> please input query: select AVG(*) from root.BHSFC.Q1.W002
select AVG(*) from root.BHSFC.Q1.W002
19:23:27.053 [main] DEBUG org.apache.iotdb.session.Session - EndPoint(ip:127.0.0.1, port:6667) execute sql select AVG(*) from root.BHSFC.Q1.W002
Export completely!

成功导出了一个 CSV 表格,内容为:

AVG(root.BHSFC.Q1.W002.WROT_HubTmp)
17.683083226110213

至此,结合上篇我们完整的了解了 IoTDB 在数据写入、导入、修改、查询、导出周期的基本操作实现和命令语句设置。但除此之外,还有一部分操作非常重要,了解它们将会让我们对 IoTDB 的整体数据结构能实现的功能有更好的了解,这就是元数据管理。

2

元数据管理

元数据是关于数据的组织、数据域及其关系的信息。元数据管理将使我们对 IoTDB 存入数据的源、目标、转换规则等有更加深入的了解,和管理调整的能力。

下面将从 5 个部分来阐释 IoTDB 中涉及的元数据管理:TsFile 管理、存储组管理、时间序列管理、元数据模板管理和节点管理。

2.1

TsFile 管理

TsFile 是 Apache IoTDB 自研的列式存储文件格式。我们可利用已有的 TsFile 文件来体验其管理操作。

首先我们进入 IoTDB 的 data 文件夹,一路点进去能找到 TsFile 后缀的文件,和以 TsFlie.resource 为后缀的文件,我们只复制那一个 TsFile 后缀的文件到其他文件夹即可,然后就可以进行下面的删除操作,最后再对复制过去的 TsFile 进行下面的加载操作。

2.1.1 删除 TsFile

我们可以使用 remove '<path>' 语句来删除 TsFile 文件,例如我们要删除 /usr/apache-iotdb-0.13.0-all-bin/data/data/sequence/root.BHSFC/0/0/ 文件夹下的 1657277482259-1-0-2.tsfile 文件,则指令为:

remove '/usr/apache-iotdb-0.13.0-all-bin/data/data/sequence/root.BHSFC/0/0/1657277482259-1-0-2.tsfile'

提示 executed successfully 即为删除成功,我们可以查询一下行数,发现变为 0 了:

IoTDB> select count(WROT_HubTmp) from root.BHSFC.Q1.W002
+-------------------------------------+
|count(root.BHSFC.Q1.W002.WROT_HubTmp)|
+-------------------------------------+
|                                    0|
+-------------------------------------+
Total line number = 1

2.1.2 加载 TsFile

加载TsFile文件的指令为:

load '<path/dir>' [autoregister=true/false][,sglevel=int][,verify=true/false]

这里的路径可以是文件路径也可以是文件夹路径,可以导入单个文件或者导入文件夹。autoregister 表示是否自动创建 schema ,参数为 true 表示自动创建 schema,相反 false 表示不创建,默认为true;sglevel 表示设定存储组级别,默认为 iotdb-datanode.properties 中设置的级别;verify 表示是否对 TsFile 中的时间序列进行元数据检查,默认为 true。开启检查时如果载入的 TsFile 中的时间序列在当前 IoTDB 中也存在,则会比较该时间序列的所有 Measurement 的数据类型是否一致,如果出现不一致将会导致载入失败,关闭该选项会跳过检查,载入更快。

我们输入如下,此前已将 TsFile 文件复制到了文件夹 /var/opt/ 下:

load '/var/opt/1657277482259-1-0-2.tsfile' autoregister=false,sglevel=1,verify=true

输入之后显示 executed successfully 则为加载成功,可以查询一下行数来验证是否已经导入成功,发现行数由0变为13834了:

IoTDB> load '/var/opt/1657277482259-1-0-2.tsfile' autoregister=false,sglevel=1,verify=true
Msg: The statement is executed successfully.
IoTDB> select count(WROT_HubTmp) from root.BHSFC.Q1.W002
+-------------------------------------+
|count(root.BHSFC.Q1.W002.WROT_HubTmp)|
+-------------------------------------+
|                                13834|
+-------------------------------------+
Total line number = 1

2.2

存储组管理

2.2.1 创建存储组

根据存储模型创建数据模型,我们可以使用 CREATE STORAGE GROUP 语句,例如创建一个名为 root.ln 的存储组:

create storage group root.ln

提示 “The statement is executed successfully” 即为创建成功:

IoTDB> create storage group root.ln
Msg: The statement is executed successfully.

2.2.2 查看存储组

我们可以使用 show storage group 语句查看存储组,SQL 语句如下:

show storage group

使用 show storage group 查看所有存储组,输出的结果如下:

IoTDB> show storage group
+-------------+
|storage group|
+-------------+
|   root.BHSFC|
+-------------+
Total line number = 1

同样也可以使用通配符简化查询,如 show storage group root.** 。

2.2.3 删除存储组

我们可以使用 delete storage group 语句删除存储组,如删除存储组 roo.BHSFC 的 SQL 语句为:

delete storage group root.BHSFC

同样我们可以使用通配符 * 简化语句。

2.2.4 统计存储组数量

我们可以使用 count storage group 语句统计存储组数量,统计所有存储组的 SQL 语句为:

count storage group

输出结果为:

IoTDB> count storage group
+-------------+
|storage group|
+-------------+
|            1|
+-------------+
Total line number = 1

同样我们可以使用通配符 * 简化语句。

2.3

时间序列管理

2.3.1 创建对齐时间序列

前面我们创建了单条时间序列 root.BHSFC.Q1.W003.speed。现在我们来创建对齐时间序列。对齐指的是不同传感器的值同时到来,即时间序列可以按一列时间戳来存储,例如我们看之前的数据模式图,设备 w002 和 w003 分属两个实体,它们之下的两条时间序列是非对齐的,即存储的时间戳和时间间隔可以不一致。而设备 wf01 下有两个传感器,status 和 temperature 可以设置为对齐时间序列。

4692a104959aa7f72371a12cc50d51b0.png

我们可以输入以下 SQL 语句创建对齐时间序列:

create aligned timeseries root.ln.wf01(status BOOLEAN encoding=PLAIN compressor=SNAPPY, temperature FLOAT encoding=PLAIN compressor=SNAPPY)

2.3.2 查询时间序列

我们可以使用 show timeseries <Path> 来查询时间序列。例如查询所有时间序列的 SQL 语句为:

show timeseries

输出结果为:

IoTDB> show timeseries
+------------------------------+-----+-------------+--------+--------+-----------+----+----------+
|                    timeseries|alias|storage group|dataType|encoding|compression|tags|attributes|
+------------------------------+-----+-------------+--------+--------+-----------+----+----------+
|      root.ln.wf01.temperature| null|      root.ln|   FLOAT|   PLAIN|     SNAPPY|null|      null|
|           root.ln.wf01.status| null|      root.ln| BOOLEAN|   PLAIN|     SNAPPY|null|      null|
|root.BHSFC.Q1.W002.WROT_HubTmp| null|   root.BHSFC|  DOUBLE| GORILLA|     SNAPPY|null|      null|
|      root.BHSFC.Q1.W003.speed| null|   root.BHSFC|   FLOAT|     RLE|     SNAPPY|null|      null|
+------------------------------+-----+-------------+--------+--------+-----------+----+----------+
Total line number = 4

查询存储组 root.BHSFC 下的时间序列,SQL 语句为:

show timeseries root.BHSFC.**

输出结果为:

IoTDB> show timeseries root.BHSFC.**
+------------------------------+-----+-------------+--------+--------+----------                                               -+----+----------+
|                    timeseries|alias|storage group|dataType|encoding|compressio                                               n|tags|attributes|
+------------------------------+-----+-------------+--------+--------+----------                                               -+----+----------+
|root.BHSFC.Q1.W002.WROT_HubTmp| null|   root.BHSFC|  DOUBLE| GORILLA|     SNAPP                                               Y|null|      null|
|      root.BHSFC.Q1.W003.speed| null|   root.BHSFC|   FLOAT|     RLE|     SNAPP                                               Y|null|      null|
+------------------------------+-----+-------------+--------+--------+----------                                               -+----+----------+
Total line number = 2

2.3.3 删除时间序列

我们可以使用 DELETE TimeSeries <PathPattern> 语句来删除时间序列,例如删除时间序列 root.ln.wf01.temperature 的 SQL 语句如下:

delete timeseries root.ln.wf01.temperature

2.3.4 统计时间序列总数

我们可以使用 COUNT TIMESERIES<Path> 来统计一条路径中的时间序列个数。例如统计所有的时间序列,SQL 语句为:

count timeseries root.**

显示结果为:

IoTDB> count timeseries root.**
+-----+
|count|
+-----+
|    3|
+-----+
Total line number = 1

2.4

元数据模板管理

元数据模板可以简化同类型实体的管理,减少元数据内存占用。

2.4.1 创建元数据模板

根据需要可以创建不同的元数据模板,例如有两个对齐的时间序列,那么可创建包含一组对齐序列的元数据模板,SQL 语句为:

create schema template t1 aligned (1 FLOAT encoding=Gorilla, 2 FLOAT encoding=Gorilla)

2.4.2 挂载元数据模板

创建之后需挂载元数据模板进行使用,这里我们选择把它应用到 root.ln 存储组的下一层级,SQL 语句为:

set schema template t1 to root.ln.wf01

此时这个模板是空的,虽然这个存储组下有两条之前创建的时间序列,但它们不会挂载到新创建的元数据模板。我们重新创建时间序列,SQL 语句为:

create timeseries of schema template on root.ln.wf01.test

这样两条共享 root.ln.wf01.test 路径的时间序列就创建好了,我们简单查询一下时间序列:

show timeseries root.ln.**

查询结果显示:

IoTDB> show timeseries root.ln.**
+------------------------+-----+-------------+--------+--------+-----------+----+----------+
|              timeseries|alias|storage group|dataType|encoding|compression|tags|attributes|
+------------------------+-----+-------------+--------+--------+-----------+----+----------+
|     root.ln.wf01.test.1| null|      root.ln|   FLOAT| GORILLA|     SNAPPY|null|      null|
|     root.ln.wf01.test.2| null|      root.ln|   FLOAT| GORILLA|     SNAPPY|null|      null|
|root.ln.wf01.temperature| null|      root.ln|   FLOAT| GORILLA|     SNAPPY|null|      null|
|     root.ln.wf01.status| null|      root.ln|   FLOAT| GORILLA|     SNAPPY|null|      null|
+------------------------+-----+-------------+--------+--------+-----------+----+----------+
Total line number = 4

可以看到两条时间序列 root.ln.wf01.test.1 和 root.ln.wf01.test.2 已经创建好了。

我们检查一下是否对齐:

show devices root.ln.wf01.**

查询结果为:

IoTDB> show devices root.ln.wf01.**
+-----------------+---------+
|          devices|isAligned|
+-----------------+---------+
|root.ln.wf01.test|     true|
+-----------------+---------+
Total line number = 1

2.4.3 查看元数据模板

查看所有元数据模板,SQL 语句为:

show schema templates

查询结果为:

IoTDB> show schema templates
+-------------+
|template name|
+-------------+
|           t1|
+-------------+
Total line number = 1

查看某个元数据模板下的物理量,SQL 语句为:

show nodes in schema template t1

查询结果为:

IoTDB> show nodes in schema template t1
+-----------+--------+--------+-----------+
|child nodes|dataType|encoding|compression|
+-----------+--------+--------+-----------+
|          1|   FLOAT| GORILLA|     SNAPPY|
|          2|   FLOAT| GORILLA|     SNAPPY|
+-----------+--------+--------+-----------+

2.4.4 卸载/删除元数据模板

卸载元数据模板,SQL 语句为:

unset schema template t1 from root.ln.wf01

此场景输入会报错,说模板正在应用:

IoTDB> unset schema template t1 from root.ln.wf01
Msg: 326: Template is in use on root.ln.wf01.test

因此我们需要先解除元数据模板的应用才能卸载,SQL 语句为:

deactivate schema template t1 from root.ln.wf01.test

这时候再卸载就成功了。

最后我们删除这个元数据模板,SQL 语句为:

drop schema template t1

2.5

节点管理

2.5.1 查看子路径

我们可以使用 SHOW CHILD PATHS pathPattern 来查看此路径模式所匹配的路径的下一层的所有路径,例如查看 root.BHSFC 的下一层:

show child paths root.BHSFC

输出为:

IoTDB> show child paths root.BHSFC
+-------------+
|  child paths|
+-------------+
|root.BHSFC.Q1|
+-------------+
Total line number = 1

2.5.2 查看子节点

我们可以使用 SHOW CHILD NODES pathPattern 查看此路径模式所匹配的节点的下一层的所有节点,例如查询 root 的下一层:

show child nodes root

输出为:

IoTDB> show child nodes root
+-----------+
|child nodes|
+-----------+
|      BHSFC|
|         ln|
+-----------+
Total line number = 2

2.5.3 统计节点数

我们可以使用 COUNT NODES <PathPattern> LEVEL=<INTEGER> 来统计当前满足某路径模式的路径中指定层级的节点个数。

9c3e1aecd7ab20350b000ada75b59405.png

结合上图的数据模式,假如我们想统计 root.BHSFC 存储组中的节点数,则 SQL 语句为:

count nodes root.BHSFC.Q1.* level=3

输出结果为:

IoTDB> count nodes root.BHSFC.Q1.* level=3
+-----+
|count|
+-----+
|    2|
+-----+
Total line number = 1

两个节点即 level 3 上 Q1 的两个分支 w002 和 w003。

下面这条 SQL 语句,则表示统计层级为 3,即路径 ln 之下所有的节点数:

count nodes root.ln.** level=3

结合数据模式图可看到应为 Wf01、status 和 temperature 共 3 个。输出结果为:

IoTDB> count nodes root.ln.** level=3
+-----+
|count|
+-----+
|    3|
+-----+
Total line number = 1

至此,我们对于 IoTDB 可以实现的基本功能已经有了全面的了解。本教程的最后一部分将涉及一个可能在 IoTDB 操作中遇到的问题及其解决方法,即本地时区设置。

3

设置时区

3.1

背景 & 问题

我们将之前例子中相同的一份 csv 的数据使用 import-csv 工具导入 IoTDB(使用默认参数),假如查询时间在 2022 年 1 月 12 日 11 点 48 分 43 秒之后的数据,只显示 10 条,输入的指令和输出结果如下:

IoTDB> select WROT_HubTmp from root.BHSFC.Q1.W002 where time>=2022-01-12T11:48:43.000 limit 10
+-----------------------------+------------------------------+
|                         Time|root.BHSFC.Q1.W002.WROT_HubTmp|
+-----------------------------+------------------------------+
|2022-01-12T11:48:43.000-08:00|                          19.2|
|2022-01-12T11:48:49.000-08:00|                          19.3|
|2022-01-12T11:49:02.000-08:00|                          19.2|
|2022-01-12T11:49:08.000-08:00|                          19.1|
|2022-01-12T11:49:59.000-08:00|                          19.0|
|2022-01-12T11:51:31.000-08:00|                      18.89999|
|2022-01-12T11:51:38.000-08:00|                          19.0|
|2022-01-12T11:51:54.000-08:00|                      18.89999|
|2022-01-12T11:52:42.000-08:00|                          19.0|
|2022-01-12T11:55:01.000-08:00|                      18.89999|
+-----------------------------+------------------------------+
Total line number = 10

可见当我们查询特定时间范围内的数据,查询结果是正常的。

但如果我们查询单行时间数据,且只限制时间格式而不指明时区查询,查询结果则可能出现问题。

如输入查询时间在 2022 年 1 月 12 日 10 点 48 分 51 秒的指令,输出结果如下:

IoTDB> select WROT_HubTmp from root.BHSFC.Q1.W002 where time=2022-01-12T10:48:51.000
+----+------------------------------+
|Time|root.BHSFC.Q1.W002.WROT_HubTmp|
+----+------------------------------+
+----+------------------------------+
Empty set.

可见查询为空,但我们的原始数据是有这个时间对应的数据的,这一行不应为空。

3.2

解决思路

IoTDB 中的数据点是以时间戳保存的,查询的时候则会以当前系统默认时区来转换成对应时间。 

范围查询正常,但是单点查询不行,这个时候一般是时区出现了问题,需要进行时区设置。

3.2.1 解决方法一

我们可以在查询的时间后面增加时区。

使用 'show version' 命令查看 IoTDB 时区,可以看到时区为 'America/Los_Angeles',即西八区(UTC/GMT -8.00)。查看时区的指令和输出结果如下:

IoTDB> show time_zone
Current time zone: America/Los_Angeles

这种情况下,我们只需在单点查询的时间最后增加 '-08:00' ,就能查询到数值了。输入的指令和输出结果如下:

IoTDB> select WROT_HubTmp from root.BHSFC.Q1.W002 where time=2022-01-12T10:48:51.000-08:00
+-----------------------------+------------------------------+
|                         Time|root.BHSFC.Q1.W002.WROT_HubTmp|
+-----------------------------+------------------------------+
|2022-01-12T10:48:51.000-08:00|                          18.3|
+-----------------------------+------------------------------+
Total line number = 1

可以看到问题得以解决,但此方法比较繁琐。

3.2.2 解决方法二

连接 IoTDB 服务器时,会使用服务器的默认时区作为 IoTDB 的默认时区,我们可以将系统的默认时区与 IoTDB 的时区保持相同。

我们可以使用 Linux 操作系统的 'timedatectl' 命令看到 'Time zone: America/Los_Angeles (PDT, -0700)'。命令和执行结果如下:

[root@localhost sbin]# timedatectl
      Local time: Mon 2022-07-04 01:37:48 PDT
  Universal time: Mon 2022-07-04 08:37:48 UTC
        RTC time: Mon 2022-07-04 08:37:48
       Time zone: America/Los_Angeles (PDT, -0700)

此时发现系统的默认时区变成了 America/Los_Angeles (PDT, -0700),我们将 IoTDB 的时区改为同样的 -0700 即可,命令和执行结果如下:

IoTDB> set time_zone=-07:00
Time zone has set to -07:00

此时我们再执行单点查询就可以正确的查出数据,命令和执行结果如下:

IoTDB> select WROT_HubTmp from root.BHSFC.Q1.W002 where time=2022-01-12T11:48:51.000
+-----------------------------+------------------------------+
|                         Time|root.BHSFC.Q1.W002.WROT_HubTmp|
+-----------------------------+------------------------------+
|2022-01-12T11:48:51.000-07:00|                          18.3|
+-----------------------------+------------------------------+
Total line number = 1

3.3

总结

PST 是 Pacific Standard Time 太平洋标准时间(-08:00)。PDT 是 Pacific Daylight Time 太平洋夏季时间(-07:00),始于每年4月的第1个周日,止于每年10月的最后一个周日。处于西八区(PST)的大部分城市都会使用夏令时。 

当操作系统的时区设置为西八区(PST)时,会自动采用夏令时(-07:00),但是 IoTDB 依然是(-08:00)时,就导致了上述可能的查询错误。 

国内用户出现上述问题一般为操作系统的时区设置错误,只需要时区设置为正确的东八区后重启 IoTDB 即可。

4

结语

到这里,IoTDB 的小白教程文档已经全部呈现完毕。通过文档,大家对于 Apache IoTDB 的应用背景、产品性质、性能优势、架构分布、功能实现应该都有了基本的认知。感谢大家的关注,希望这个系列文档能够更好地帮助大家了解 IoTDB,使用IoTDB!

a0e48ba2e95a273d3c450c1a0e383f63.gif

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐