小 T 导读:TDengine 是专为时序数据而研发的大数据平台,存储和计算都针对时序数据的特点量身定制,在反对规范 SQL 的根底之上,还提供了一系列贴合时序业务场景的特色查问语法,极大中央便了时序场景的利用开发。TDengine 提供的特色查问包含数据切分查问和窗口切分查问,本文将从语法层面深刻解读这两种特色查问。
数据切分查问
依据业务须要,有时咱们须要按肯定的维度对数据进行切分,当在切分出的数据空间内进行一系列的计算时,就须要应用数据切分子句,语法如下:PARTITION BY part_list
在上述语法中,part_list 能够是任意的标量表达式,包含列、常量、标量函数和它们的组合。
时序数据库(Time Series Database)TDengine 按如下形式解决数据切分子句:
- 数据切分子句位于 WHERE 子句之后;
- 数据切分子句将表数据按指定的维度进行切分,每个切分的分片进行指定的计算。计算由之后的子句定义(窗口子句、GROUP BY 子句或 SELECT 子句);
- 数据切分子句能够和窗口切分子句(或 GROUP BY 子句)一起应用,此时前面的子句作用在每个切分的分片上。例如,将数据按标签 location 进行分组,并对每个组按 10 分钟进行降采样,取其最大值。
select max(current) from meters partition by location interval(10m)
数据切分子句最常见的用法就是在超级表查问中,按标签将子表数据进行切分,而后别离进行计算。特地是 PARTITION BY TBNAME 用法,它将每个子表的数据独立进去,造成一条条独立的工夫序列,极大中央便了各种时序场景的统计分析。
窗口切分查问
TDengine 反对按时间段窗口切分形式进行聚合后果查问,比方温度传感器每秒采集一次数据,但需查问每隔 10 分钟的温度平均值,这种场景下能够应用窗口子句来取得须要的查问后果。想要让查问的数据汇合依照窗口切分成查问子集并进行聚合,就须要用到窗口子句,窗口蕴含工夫窗口(time window)、状态窗口(status window)、会话窗口(session window)三种窗口。其中工夫窗口又可划分为滑动工夫窗口和翻转工夫窗口。窗口切分查问语法如下:
SELECT select_list FROM tb_name
[WHERE where_condition]
[SESSION(ts_col, tol_val)]
[STATE_WINDOW(col)]
[INTERVAL(interval [, offset]) [SLIDING sliding]]
[FILL({NONE | VALUE | PREV | NULL | LINEAR | NEXT})]
窗口子句的规定
- 窗口子句位于数据切分子句之后,GROUP BY 子句之前,且不能够和 GROUP BY 子句一起应用;
-
窗口子句将数据按窗口进行切分,对每个窗口进行 SELECT 列表中表达式的计算,SELECT 列表中的表达式只能蕴含:
- 常量
- _wstart 伪列、_wend 伪列和_wduration 伪列
- 汇集函数(包含选择函数和能够由参数确定输入行数的时序特有函数)
- 蕴含下面表达式的表达式
- 且至多蕴含一个汇集函数
- 窗口子句不能够和 GROUP BY 子句一起应用;
- WHERE 语句能够指定查问的起止工夫和其余过滤条件。
FILL 子句
FILL 语句指定的是某一窗口区间数据缺失状况下的填充模式。填充模式包含以下几种:
- 不进行填充:NONE(默认填充模式);
- VALUE 填充:固定值填充,此时须要指定填充的数值。例如:FILL(VALUE, 1.23)。这里须要留神,最终填充的值受由相应列的类型决定,如 FILL(VALUE, 1.23),相应列为 INT 类型,则填充值为 1;
- PREV 填充:应用前一个非 NULL 值填充数据,例如:FILL(PREV);
- NULL 填充:应用 NULL 填充数据,例如:FILL(NULL);
- LINEAR 填充:依据前后间隔最近的非 NULL 值做线性插值填充,例如:FILL(LINEAR);
- NEXT 填充:应用下一个非 NULL 值填充数据,例如:FILL(NEXT)。
在应用 FILL 子句时,须要留神:
- 应用时可能生成大量的填充输入,因而务必指定查问的工夫区间。针对每次查问,零碎可返回不超过 1 千万条具备插值的后果。
- 在进行工夫维度聚合时,返回的后果中工夫序列严格枯燥递增。
- 如果查问对象是超级表,则聚合函数会作用于该超级表下满足值过滤条件的所有表的数据。如果查问中没有应用 PARTITION BY 语句,则返回的后果依照工夫序列严格枯燥递增;如果查问中应用了 PARTITION BY 语句分组,则返回后果中每个 PARTITION 内不会依照工夫序列严格枯燥递增。
工夫窗口
工夫窗口又可分为滑动工夫窗口和翻转工夫窗口。INTERVAL 子句用于产生相等工夫周期的窗口,SLIDING 用以指定窗口向前滑动的工夫,在执行工夫窗口查问时,其会随着工夫流动向前滑动。在定义间断查问时咱们须要指定工夫窗口(time window)大小和每次前向增量工夫(forward sliding times)。
如上图,[t0s, t0e]、[t1s , t1e]、[t2s, t2e] 别离是执行三次间断查问的工夫窗口范畴,窗口的前向滑动的工夫范畴以 sliding time 标识。查问过滤、聚合等操作依照每个工夫窗口为独立的单位执行。当 SLIDING 与 INTERVAL 相等的时候,滑动窗口即为翻转窗口。
INTERVAL 和 SLIDING 子句须要配合聚合和选择函数来应用。以下 SQL 语句非法:SELECT * FROM temp_tb_1 INTERVAL(1m);
SLIDING 的向前滑动的工夫不能超过一个窗口的工夫范畴。以下语句非法:
SELECT COUNT(*) FROM temp_tb_1 INTERVAL(1m) SLIDING(2m);
应用工夫窗口须要留神:
- 聚合时间段的窗口宽度由关键词 INTERVAL 指定,最短时间距离 10 毫秒(10a);并且反对偏移 offset(偏移必须小于距离),也即工夫窗口划分与“UTC 时刻 0”相比的偏移量。SLIDING 语句用于指定聚合时间段的前向增量,也即每次窗口向前滑动的时长。
- 应用 INTERVAL 语句时,除非极非凡的状况,都要求把客户端和服务端的 taos.cfg 配置文件中的 timezone 参数配置为雷同的取值,以防止工夫处理函数频繁进行跨时区转换而导致的重大性能影响。
- 返回的后果中工夫序列严格枯燥递增。
状态窗口
TDengine 应用整数(布尔值)或字符串来标识产生记录时设施的状态量。产生的记录如果具备雷同的状态量数值则归属于同一个状态窗口,数值扭转后该窗口敞开。如上图所示,依据状态量咱们可能确定的状态窗口别离是 [2019-04-28 14:22:07,2019-04-28 14:22:10] 和 [2019-04-28 14:22:11,2019-04-28 14:22:12] 两个。
咱们能够应用 STATE_WINDOW 来确定状态窗口划分的列。例如:
SELECT COUNT(*), FIRST(ts), status FROM temp_tb_1 STATE_WINDOW(status);
会话窗口
SELECT COUNT(*), FIRST(ts) FROM temp_tb_1 SESSION(ts, tol_val);
示例
以智能电表为例,其建表语句如下:
CREATE TABLE meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT);
针对智能电表采集的数据,以 10 分钟为一个阶段,计算过来 24 小时的电流数据的平均值、最大值、电流的中位数。如果没有计算值,则用前一个非 NULL 值填充。应用的查问语句如下:
SELECT AVG(current), MAX(current), APERCENTILE(current, 50) FROM meters
WHERE ts>=NOW-1d and ts<=now
INTERVAL(10m)
FILL(PREV);
写在最初
除了数据量大、构造绝对简略的特点外,时序数据在查问场景中还大量波及工夫戳的解决,在很多业务场景的采集数据时,都须要按工夫戳进行分组与计算,如果依照惯例模式将原始数据读入内存,再由应用层程序去解决工夫窗口划分的逻辑,就会因读取海量原始时序数据导致磁盘 IO、CPU 及内存开销的重大节约,还会晋升业务层代码复杂度。
但如果咱们可能把握并灵活运用 TDengine 所提供的上述时序数据特色查问性能,联合业务场景抉择相应的函数就能将相干计算负荷下沉到数据库层,在晋升零碎响应性能的同时也缩小了系统资源的节约。
想理解更多 TDengine Database 的具体细节,欢送大家在 GitHub 上查看相干源代码。