前言
上篇教程介绍了 Apache IoTDB 处理时序数据时,能够实现的部分具体功能和具体的操作命令,包括数据导入、基本查询、和聚合查询。
本篇将继续介绍 Apache IoTDB 可实现的其他功能和相关 SQL 语句命令,包括数据的写入、删除、导出、元数据操作和时区设置的注意事项。
1
数据写入、删除与导出
1.1
插入数据
物联网场景下,元件产生数据将自动写入,但有时候,如果过去的一些数据需要修改,可以使用 insert 语句插入修改后的值,覆盖原数据。
例如,我们可以向已有的时间序列 root.BHSFC.Q1.W002.speed 中插入单行数据,SQL 语句如下:
代码语言:javascript复制insert into root.BHSFC.Q1.W003(timestamp,speed) values(1657472400000,2)
我们可以使用查询语句查看是否插入成功:
代码语言:javascript复制select speed from root.BHSFC.Q1.W003
显示结果如下,可知已插入成功:
代码语言:javascript复制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 语句:
代码语言:javascript复制delete from root.BHSFC.Q1.W003.speed
我们可以验证一下,输入查询语句 select speed from root.BHSFC.Q1.W003 ,输出结果已经没有数据了,说明删除成功了:
代码语言:javascript复制```
IoTDB> select speed from root.BHSFC.Q1.W003
---- ------------------------------
|Time|root.BHSFC.Q1.W003.speed|
---- ------------------------------
---- ------------------------------
Empty set.
```
1.2.1.2 删除时间范围内的数据
也可以与 where 语句结合删除某个时间片段,如删除 2022 年 1 月 14 日零点之后的数据:
代码语言:javascript复制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 语句为:
代码语言:javascript复制set ttl to root.BHSFC.Q1.W003 3600000
这条语句表示在 root.BHSFCQ1.W003 设备中,最近一个小时的数据将会保存,旧数据会被删除。
1.2.2.2 取消 TTL
我们也可以取消这条时间序列的 TTL ,SQL 语句为:
代码语言:javascript复制unset ttl to root.BHSFC.Q1.W003
1.2.2.3 显示 TTL
我们还可以查询目前已设置的 TTL ,SQL 语句为:
代码语言:javascript复制show all ttl
输出的结果为:
代码语言:javascript复制 ------------- ----
|storage group| ttl|
------------- ----
| root.ln|null|
| root.BHSFC|null|
------------- ----
Total line number = 2
未设置 TTL 的存储组的 TTL 将显示为 null。
1.3
导出数据
我们可以使用 CSV 工具将数据导出。首先进入 tools 目录,然后输入 SQL 语句:
代码语言:javascript复制./export-csv.sh -h 127.0.0.1 -p 6667 -u root -pw root -td ./
之后会出现提示输入查询语句,我们按需输入 SQL 语句即可。例如我们要导出测试数据的平均值,则输入:
代码语言:javascript复制select AVG(*) from root.BHSFC.Q1.W002
结果显示如下:
代码语言:javascript复制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 表格,内容为:
代码语言:javascript复制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 文件,则指令为:
代码语言:javascript复制remove '/usr/apache-iotdb-0.13.0-all-bin/data/data/sequence/root.BHSFC/0/0/1657277482259-1-0-2.tsfile'
提示 executed successfully 即为删除成功,我们可以查询一下行数,发现变为 0 了:
代码语言:javascript复制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文件的指令为:
代码语言:javascript复制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/ 下:
代码语言:javascript复制load '/var/opt/1657277482259-1-0-2.tsfile' autoregister=false,sglevel=1,verify=true
输入之后显示 executed successfully 则为加载成功,可以查询一下行数来验证是否已经导入成功,发现行数由0变为13834了:
代码语言:javascript复制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 的存储组:
代码语言:javascript复制create storage group root.ln
提示 “The statement is executed successfully” 即为创建成功:
代码语言:javascript复制IoTDB> create storage group root.ln
Msg: The statement is executed successfully.
2.2.2 查看存储组
我们可以使用 show storage group 语句查看存储组,SQL 语句如下:
代码语言:javascript复制show storage group
使用 show storage group 查看所有存储组,输出的结果如下:
代码语言:javascript复制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 语句为:
代码语言:javascript复制delete storage group root.BHSFC
同样我们可以使用通配符 * 简化语句。
2.2.4 统计存储组数量
我们可以使用 count storage group 语句统计存储组数量,统计所有存储组的 SQL 语句为:
代码语言:javascript复制count storage group
输出结果为:
代码语言:javascript复制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 可以设置为对齐时间序列。
我们可以输入以下 SQL 语句创建对齐时间序列:
代码语言:javascript复制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 语句为:
代码语言:javascript复制show timeseries
输出结果为:
代码语言:javascript复制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 语句为:
代码语言:javascript复制show timeseries root.BHSFC.**
输出结果为:
代码语言:javascript复制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 语句如下:
代码语言:javascript复制delete timeseries root.ln.wf01.temperature
2.3.4 统计时间序列总数
我们可以使用 COUNT TIMESERIES<Path> 来统计一条路径中的时间序列个数。例如统计所有的时间序列,SQL 语句为:
代码语言:javascript复制count timeseries root.**
显示结果为:
代码语言:javascript复制IoTDB> count timeseries root.**
-----
|count|
-----
| 3|
-----
Total line number = 1
2.4
元数据模板管理
元数据模板可以简化同类型实体的管理,减少元数据内存占用。
2.4.1 创建元数据模板
根据需要可以创建不同的元数据模板,例如有两个对齐的时间序列,那么可创建包含一组对齐序列的元数据模板,SQL 语句为:
代码语言:javascript复制create schema template t1 aligned (1 FLOAT encoding=Gorilla, 2 FLOAT encoding=Gorilla)
2.4.2 挂载元数据模板
创建之后需挂载元数据模板进行使用,这里我们选择把它应用到 root.ln 存储组的下一层级,SQL 语句为:
代码语言:javascript复制set schema template t1 to root.ln.wf01
此时这个模板是空的,虽然这个存储组下有两条之前创建的时间序列,但它们不会挂载到新创建的元数据模板。我们重新创建时间序列,SQL 语句为:
代码语言:javascript复制create timeseries of schema template on root.ln.wf01.test
这样两条共享 root.ln.wf01.test 路径的时间序列就创建好了,我们简单查询一下时间序列:
代码语言:javascript复制show timeseries root.ln.**
查询结果显示:
代码语言:javascript复制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 已经创建好了。
我们检查一下是否对齐:
代码语言:javascript复制show devices root.ln.wf01.**
查询结果为:
代码语言:javascript复制IoTDB> show devices root.ln.wf01.**
----------------- ---------
| devices|isAligned|
----------------- ---------
|root.ln.wf01.test| true|
----------------- ---------
Total line number = 1
2.4.3 查看元数据模板
查看所有元数据模板,SQL 语句为:
代码语言:javascript复制show schema templates
查询结果为:
代码语言:javascript复制IoTDB> show schema templates
-------------
|template name|
-------------
| t1|
-------------
Total line number = 1
查看某个元数据模板下的物理量,SQL 语句为:
代码语言:javascript复制show nodes in schema template t1
查询结果为:
代码语言:javascript复制IoTDB> show nodes in schema template t1
----------- -------- -------- -----------
|child nodes|dataType|encoding|compression|
----------- -------- -------- -----------
| 1| FLOAT| GORILLA| SNAPPY|
| 2| FLOAT| GORILLA| SNAPPY|
----------- -------- -------- -----------
2.4.4 卸载/删除元数据模板
卸载元数据模板,SQL 语句为:
代码语言:javascript复制unset schema template t1 from root.ln.wf01
此场景输入会报错,说模板正在应用:
代码语言:javascript复制IoTDB> unset schema template t1 from root.ln.wf01
Msg: 326: Template is in use on root.ln.wf01.test
因此我们需要先解除元数据模板的应用才能卸载,SQL 语句为:
代码语言:javascript复制deactivate schema template t1 from root.ln.wf01.test
这时候再卸载就成功了。
最后我们删除这个元数据模板,SQL 语句为:
代码语言:javascript复制drop schema template t1
2.5
节点管理
2.5.1 查看子路径
我们可以使用 SHOW CHILD PATHS pathPattern 来查看此路径模式所匹配的路径的下一层的所有路径,例如查看 root.BHSFC 的下一层:
代码语言:javascript复制show child paths root.BHSFC
输出为:
代码语言:javascript复制IoTDB> show child paths root.BHSFC
-------------
| child paths|
-------------
|root.BHSFC.Q1|
-------------
Total line number = 1
2.5.2 查看子节点
我们可以使用 SHOW CHILD NODES pathPattern 查看此路径模式所匹配的节点的下一层的所有节点,例如查询 root 的下一层:
代码语言:javascript复制show child nodes root
输出为:
代码语言:javascript复制IoTDB> show child nodes root
-----------
|child nodes|
-----------
| BHSFC|
| ln|
-----------
Total line number = 2
2.5.3 统计节点数
我们可以使用 COUNT NODES <PathPattern> LEVEL=<INTEGER> 来统计当前满足某路径模式的路径中指定层级的节点个数。
结合上图的数据模式,假如我们想统计 root.BHSFC 存储组中的节点数,则 SQL 语句为:
代码语言:javascript复制count nodes root.BHSFC.Q1.* level=3
输出结果为:
代码语言:javascript复制IoTDB> count nodes root.BHSFC.Q1.* level=3
-----
|count|
-----
| 2|
-----
Total line number = 1
两个节点即 level 3 上 Q1 的两个分支 w002 和 w003。
下面这条 SQL 语句,则表示统计层级为 3,即路径 ln 之下所有的节点数:
代码语言:javascript复制count nodes root.ln.** level=3
结合数据模式图可看到应为 Wf01、status 和 temperature 共 3 个。输出结果为:
代码语言:javascript复制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 条,输入的指令和输出结果如下:
代码语言:javascript复制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 秒的指令,输出结果如下:
代码语言:javascript复制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)。查看时区的指令和输出结果如下:
代码语言:javascript复制IoTDB> show time_zone
Current time zone: America/Los_Angeles
这种情况下,我们只需在单点查询的时间最后增加 '-08:00' ,就能查询到数值了。输入的指令和输出结果如下:
代码语言:javascript复制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)'。命令和执行结果如下:
代码语言:javascript复制[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 即可,命令和执行结果如下:
代码语言:javascript复制IoTDB> set time_zone=-07:00
Time zone has set to -07:00
此时我们再执行单点查询就可以正确的查出数据,命令和执行结果如下:
代码语言:javascript复制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!