一文帮你掌握 TDengine 的降采样查询 + 跨时区统计
作者|陈玉,涛思数据
小 T 导读:作为一款高性能的时序数据库,TDengine 提供了强大的数据分析功能。在TDengine官网的第一个章节里,有这样的描述:“无论是十年前还是一秒钟前的数据,指定时间范围即可查询。数据可在时间轴上或多个设备上进行聚合。”
今天,我们的主角就是上文中“可在时间轴上”聚合的强大函数——INTERVAL。
INTERVAL 是 TDengine 一大重要功能,可以帮助我们实现降低数据采集频率的功能——也就是降采样。举个简单的例子:假设我们有某个设备一年的数据,时间数据的频率是 1 天,那么就是一共 365 条数据。现在,如果我们想按照‘月’这个频率统计,那么数据量就变成了 12 条。
根据官网的语法描述,相关的功能模块有三个:
INTERVAL 本身
SLIDING
INTERVAL OFFSET
对于以处理时序数据为根基的时序数据库来说,如何灵活的利用时间频率来计算分析数据实在是太重要了。下面我们围绕上面三个功能模块,分别举一个简单的应用场景的例子并做出具体说明:
INTERVAL:查询温度传感器 t1 记录的温度、压力每五分钟的平均值
这是一个最简单的使用情况,INTERVAL 负责指定时间范围窗口,由 AVG 这种聚合函数来计算这个时间范围内的平均值。也可以换成 MAX/MIN 这类的选择函数,来统计出这个时间范围内的最大值/最小值。(在 TAOS SQL 中,聚合函数指的是 COUNT/AVG/TWA/SUM 等用于从数据集中汇合再计算的函数,选择函数是指 MIN/MAX/FIRST/LAST/LAST_ROW 等用于从数据集中筛选结果的函数。)
INTERVAL 本质上就是 group by 的时间版本,所以一定需要配合上述聚合或选择函数来使用。INTERVAL 后面的时间单位可以是 a(毫秒)、s(秒)、m(分)、h(小时)、d(自然日)、w(周), n(自然月) 和 y(自然年)。(最小 10 毫秒,暂时还不支持自然周,interval(1w) 目前等效于 interval(7d))。
SLIDING:可以统计类似股票市场的均线
上述语句的实际含义是统计股市上某股票所有每过一天的 5 天的价格平均值,把这些值连起来,就是大家熟知的五日均线了。
在上述计算过程中,SLIDING 起到了非常关键的作用。我们已经知道,INTERVAL 的值负责指定每次执行查询的时间窗口。SLIDING 则代表着指定窗口向前滑动的时间。如下图所示:
t0s,t1s,t2s 分别是三个时间窗口的起点,t0e,t1e,t2e 分别是三个时间窗口的终点。当我们在查询中不指定 SLIDING 的值时,它默认等于 INTERVAL VALUE。也即是说在第一个例子当中的 select avg(t), avg(p) from t1 interval(5m)等效于 select avg(t), avg(p) from t1 interval(5m) sliding (5m);
INTERVAL OFFSET:统计某个设备在其他时区(向西相差三个时区)的一个月的总数据量
这个 SQL 的语义是:统计当前服务端所在时区向西推动三个时区后的这个设备的该月总数据。OFFSET 3h 代表的是 OFFSET 值为 3h,也就是说这个 SQL 适用于统计不同时区的自然月数据统计。
INTERVAL OFFSET 会相对复杂一些。想了解的话,需要先更多地了解 INTERVAL 和时区的关系。
如果 INTERVAL 的值是自然日(d),自然月(n),自然年(y),那么它就是对齐 TDengine 服务端所在时区的 0 点开始做的窗口切分,如下图所示:不论当前所属哪个时区,所有时间戳列的起始时间都是 0 点。
但是,如果是以时分秒及以下的时间单位去切分窗口,那么 INTERVAL 的值则是对齐从 UTC-0 时间的 00:00:00.000 开始切分的时间窗口。
如下图所示,时间戳列的起始时间都是 8 点。这是因为 TDengine 服务端所在的服务器上的时区为 UTC-8,标准时间为 0 点的时候,东八区为 8 点(注意:在 POSIX 标准中,UTC-8 代表东八区,与平时的习惯性表达不一样)。
这个场景,就是官网文档中这段话的含义:
TDengine 中时间戳的时区总是由客户端进行处理,与服务端无关。具体来说,客户端会对 SQL 语句中的时间戳进行时区转换,转为 UTC-0 时区的 Unix 时间戳再交由服务端进行写入和查询;在读取数据时,服务端也是采用 UTC-0 时区提供的原始数据,客户端收到后再根据本地设置,把时间戳转换为本地系统所要求的时区进行显示。
总结一下就是:TDengine 以时间戳形式来存储时间数据,时间戳本身是一个和时区无关的东西,但是由于 TDengine 要把数据查询出来展示给世界上不同地区的用户看,就和时区有关系了。在 INTERVAL 中,如果是自然日,自然月,自然年,均以 TDengine 服务端所在时区的 0 点为起始时间进行时间窗口区分。如果是以 h(小时)及以下为单位切分窗口,那么进行窗口切分的起始时间就是 UTC 时区的 0 点。
不论是哪个时区的客户端,最终的计算列结果都是一致的,只是由于时区不同,所以显示的时候在时间戳列上会有一些偏差。因此我们强烈建议,非特殊情况下,客户端服务器和服务端服务器的时区要保持一致,从而使得两边的查询显示是一致的,从而减少不必要的误解。
比如,左侧客户端 count(*)看起来应该是 1 4 2,实际上却是 4 和 3。这就是因为两边时区不一致导致的视觉差异。这时候就要以服务端的显示为准:6 月 30 日有 4 条数据,7 月 1 日有 3 条数据——TDengine 服务端所在服务器的查询结果,永远是所见即所得的正确。
搞清楚了 INTERVAL 对于自然日月年和时分秒的不同切分逻辑后,接下来,我们终于可以说一下 OFFSET 了。
OFFSET 其实是 INTERVAL 功能的偏移值。通过调整 OFFSET 值,就可以在时间轴上自由选择时间窗口的起始点。从而完成不同时区的数据分析统计。
比如,服务端当前的时区是东八区,但是我们想知道在东五区时区下,设备 t1 的每个月数据总量。就可以这样写:
但是由于 OFFSET 目前暂时不能支持负值,所以时间窗口的起点只能从时区东向西偏移。因此如果想用时区偏移功能统计 24 个时区,暂时可以把服务端所在的服务器时区设置为东十二区(UTC-12)。
作为一个定位国际化的产品,我们TDengine后面会继续完善相关的功能。如果想了解更多更具体的细节,可以在GitHub上查看相关源代码。
版权声明: 本文为 InfoQ 作者【TDengine】的原创文章。
原文链接:【http://xie.infoq.cn/article/5f9e7ec100bbeb41401a36cb7】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论