本文介绍TSQL语法和功能。

TSQL支持如下的数据类型

类型 含义 合法值
Boolean 布尔值。 True或者False
SMALLINT 2个字节表示的有符合的整数类型。 范围从-32,768到32,767
INTEGER or INT 4个字节表示的有符号的整数类型。 范围从-2,147,483,648到2,147,483,647
BIGINT 8个字节表示的有符号的整数类型。 合法的值的范围从-9,223,372,036,854,775,808 到9,223,372,036,854,775,807
FLOAT 4个字节表示的单精度浮点数。
DOUBLE 8个字节表示的双精度浮点数。TSDB中的指标值如果是浮点类型,则TSQL缺省处理成双精度浮点数。
VARCHAR UTF-8编码的字符类型。
Timestamp 时间戳类型,输入格式是’yyyy-MM-dd HH:mm:ss.SSS’, 其组成部分有年,月,日,小时,分钟,秒,毫秒。 从1970年1月1日零点开始
Date 年月日,以YYYY-MM-DD为格式。 2019-03-01
Time 24小时内的小时,分钟,秒,以hh:mm:ss为格式。 10:05:03

2. TSQL查询语法

TSQL的查询分为两种:元数据的查询,时序数据的查询。

2.1 元数据查询

2.1.1 查询TSDB的数据表(度量metric)

使用“SHOW TABLES”来获取数据表。

SHOW TABLES FROM TSDB

如果你的TSDB实例有大量的metric, 使用上面的命令需要比较长的延迟。在这种情况下,建议使用TSDB引擎的/api/suggest来查询特定的metric。

2.1.2 查询TSDB中名字符合条件的数据表(度量metric)

下面的例子查询所有名字以cpu为前缀的数据表的名字。

SHOW TABLES FROM TSDB WHERE TABLE_NAME like 'cpu%'

2.1.3 查询数据表下的列的信息

使用”DESCRIBE”来获取一个数据表下的所有列的信息,包括列名,列的数据类型。

DESCRIBE TSDB.`cpu.usage_system`
+----------------------+--------------------+--------------+
|     COLUMN_NAME      |     DATA_TYPE      | IS_NULLABLE  |
+----------------------+--------------------+--------------+
| hostname             | CHARACTER VARYING  | YES          |
| rack                 | CHARACTER VARYING  | YES          |
| service_environment  | CHARACTER VARYING  | YES          |
| os                   | CHARACTER VARYING  | YES          |
| service              | CHARACTER VARYING  | YES          |
| datacenter           | CHARACTER VARYING  | YES          |
| arch                 | CHARACTER VARYING  | YES          |
| service_version      | CHARACTER VARYING  | YES          |
| team                 | CHARACTER VARYING  | YES          |
| region               | CHARACTER VARYING  | YES          |
| timestamp            | TIMESTAMP          | NO           |
| value                | DOUBLE             | YES          |
+----------------------+--------------------+--------------+

2.2 TSQL时序数据查询

TSQL的时序数据查询是通过SELECT语句,一个SELECT语句包含了查询的表达式,FROM子句,WHERE子句, GROUPBY子句,ORDERBY子句等标准SQL常见的构成部分。下面简要列出了TSQL的SELECT语句的结构。

select_stmt = 
        SELECT exp_list 
        FROM from_clause 
        [where_clause]  
        [groupby_clause] 
        [having_clause] 
        [orderby_clause] 
        [limit_clause]

2.2.1 TSQL的保留字

请参考SQL标准获取完整的SQL保留字。如果查询中的作为表,列或者函数名字的标识符含有特殊字符或者保留字,则需要将含有特殊字符或者保留字的标识符用转义字符`,这样的处理和MySQL的词法分析是相同的。比如,timestamp, value,这两个是保留字,在查询中使用他们,需要以如下形式进行。

SELECT `timestamp`, `value` from tsdb.table_name

又比如,度量cpu.usage_system里含有.这样的特殊符号,在查询这样的度量的时候,需要以如下形式进行。

SELECT * from tsdb.`cpu.usage_system`

3. TSQL时序查询功能介绍

3.1 条件过滤 Filter

3.1.1 时间维度的条件过滤

时间维度上的条件,指包含timestamp这个列上条件,可以有下面几种形式:

Filter 含义 备注
timestamp between ‘2019-03-01 00:00:00’ and ‘2019-03-01 00:00:10’ 指定时间范围的下界和上界 范围上界和下界都是以’yyyy-MM-dd HH:mm:ss.SSS’的格式 , 时间戳字符串默认UTC时区。
timestamp between 1551398400000 and 1551398410000 指定时间范围的下界和上界 上界和下界以Epoch毫秒的格式。Epoch毫秒以long型的整数形式提供,不能以字符串形式提供。参考/api/query中对“start”,“end”的描述。
timestamp >= ‘2019-03-01 00:00:00’ 指定时间范围的下界 “yyyy-MM-dd HH:mm:ss.SSS”的格式
timestamp >= 1551398400000 指定时间范围的下界 Epoch毫秒的格式。
timestamp <= ‘2019-03-01 00:00:10’ 指定时间范围的上界 “yyyy-MM-dd HH:mm:ss.SSS”的格式。
timestamp < 1551398410000 指定时间范围的上界 Epoch毫秒的格式。
timestamp >= ‘2019-03-01 00:00:00’ and `timestamp` <= ‘2019-03-01 00:00:10’ 指定时间范围的下界和上界 “yyyy-MM-dd HH:mm:ss.SSS”的格式。
timestamp >=1551398400000 and `timestamp` <=1551398410000 指定时间范围的下界和上界 Epoch毫秒的格式。
timestamp between date_diff(now(), interval ‘5’ minute) and now() date_diff计算得到当前时间之前5分钟的时间戳,作为下界, now()函数返回当前时间,作为时间范围上界
timestamp >= date_diff(now(), interval ‘5’ minute) and timestamp <= now() date_diff计算得到当前时间之前5分钟的时间戳,作为下界, now()函数返回当前时间,作为时间范围上界

需要注意的几点:

  • 时间戳字符串“yyyy-MM-dd HH:mm:ss.SSS”默认是UTC时区。
  • 时间戳上下界也可以Epoch毫秒形式提供。必须是long型的整数形式,精度是毫秒,不能以字符串表示。
  • 时间维度上的条件过滤是TSQL查询必须的,如果没有指定要查询的时间范围,TSQL可能会扫描大量的数据,造成系统性能上的影响。
  • 在指定时间范围的条件中,下界或上届的时间戳常量是必须以“yyyy-MM-dd HH:mm:ss.SSS”格式输入。如果格式有误,TSQL无法解析成正确的时间戳常量,而是处理成varchar, 则查询返回不是想要的结果。

3.1.2 TSQL时间戳时区 (Timestamp timezone)

3.1.2.1 TSDB2.5.9版本之前的时区支持

在TSDB 2.5.9之前的版本(可通过/api/version查询TSDB数据库版本号):

  • 时间戳字符串输入: TSQL查询中出现的时间戳字符串以“yyyy-MM-dd HH:mm:ss.SSS库版本号: 时间戳字符”, 并且默认是UTC时区。所以如果是“2019-03-01 00:00:00.000”这样的时间戳字符串,被解析成GMT标准时间“2019-03-01 00:00:00.00”,也就是北京时间“2019-03-01 08:00:00.00”。
  • 时间戳类型输出:时间戳类型的列的输出也是以UTC为默认时区。

示例:

select hostname, `timestamp`, `value` 
from tsdb.`cpu.usage_system` 
where `timestamp` between '2019-03-01 00:00:00' and '2019-03-01 00:00:10' 
limit 2;
+-----------+------------------------+---------------------+
| hostname  |       timestamp        |        value        |
+-----------+------------------------+---------------------+
| host_7    | 2019-03-01 00:00:00.0  | 14.568605774857241  |
| host_7    | 2019-03-01 00:00:10.0  | 14.722472083963408  |
+-----------+------------------------+---------------------+

上面的例子的查询,是查询GMT标准时间“2019-03-01 00:00:00”到GMT标准时间“2019-03-01 00:00:10”的记录,在输出的timestamp列中,也是以GMT标准时间。

3.1.2.2 TSDB2.5.9版本的时区支持

TSDB2.5.9引入了对于非UTC时区的支持:

  • 时间戳字符串输入:时间戳字符串支持“yyyy-MM-dd HH:mm:ss.SSS ZZZ”的格式,时区offset是可选的。北京时间的表示:时区offset设置成“+0800”。比如“2019-03-01 08:00.00 +0800”表示北京时间2019年3月1日早上8点。
  • 时间戳类型输出:引入新函数localtime(timestamp, timezoneOffset),来获取指定时区内的本地时间。比如,如果要获得北京时间, 使用localtime(timestamp, ‘+0800’)。

例子:

select hostname, localtime(`timestamp`, '+0800') as ltime, `value` 
from tsdb.`cpu.usage_system` 
where `timestamp` between '2019-03-01 08:00:00 +0800' and '2019-03-01 08:00:10 +0800' 
limit 2;
+-----------+------------------------+---------------------+
| hostname  |         ltime          |        value        |
+-----------+------------------------+---------------------+
| host_7    | 2019-03-01 08:00:00.0  | 14.568605774857241  |
| host_7    | 2019-03-01 08:00:10.0  | 14.722472083963408  |
+-----------+------------------------+---------------------+

上面的例子中的查询,通过在时间戳字符串上使用“+0800”, 以及使用localtime()函数,查询的是北京时间 2019年3月1日早上8点到8点零10秒的记录。查询结果也是以北京时间显示。可以看到,上面两个例子,实际上查询获得的是同样的记录集,只是时间戳字符串时区显示不同。

在2.5.9版本及之后,TSDB继续支持2.5.9版本之前的查询语义,如果你的应用里已经使用了UTC时区的查询,可以继续使用。

函数localtime()也可以作用到tumble()函数的结果上。如下面的例子所示:

select 
    count(*) as cnt,   
    localtime(tumble(`timestamp`, interval '5' second),  '+0800') as ltime 
from tsdb.`cpu.usage_system` 
where `timestamp` between '2019-03-01 08:00:00 +0800' and '2019-03-01 08:00:10 +0800' 
group by ltime;
+------+------------------------+
| cnt  |         ltime          |
+------+------------------------+
| 10   | 2019-03-01 08:00:00.0  |
| 10   | 2019-03-01 08:00:10.0  |
+------+------------------------+

3.1.3 tag上的条件过滤

tag对应的列上,可以支持多种形式的条件, 即支持简单的相等比较,不等比较,范围比较,也支持多个值(In List)的比较,也支持对tag对应的列上进行函数计算后比较的过滤条件。并且,以上这些过滤条件可以以任意形式被逻辑运算符AND/OR组合起来,形成功能强大的条件过滤。

条件类型 例子 含义
等值比较 hostname = ‘host_0’ 寻找hostname是“host_0”的时间线数据。
多值列表比较 hostname in (‘host_0’, ‘host_1’, ‘host_2’) 寻找hostname是“host_0”, “host_1”, “host_2”的时间线数据。
范围比较 hostname >= ‘host_0’ 寻找hostname大于等于“host_0”的时间线数据。
String函数比较 substr(hostname, 2, 4) = ‘ost’ 寻找hostname的第2-4个字符是“ost”的时间线。
String函数比较 upper(substr(hostname, 1,4))=’HOST’ 寻找hostname的前4个字符转化成大写字符后是“HOST”的时间线,包括“host”, “Host”, “HOST”, “hOST”等等。
整数函数比较 length(hostname) = 5 寻找hostname是由5个字符组成的时间线。

3.1.4 度量值(metric value)上的条件过滤

TSQL同样支持在度量值这个列上的过滤条件,用户可以用下面的方式进行度量值上的条件过滤。

条件类型 例子 含义
等值比较 value = 100.0 查询度量值等于100的数据点。
范围比较 value >= 5.0 and value < = 60.0 查询度量值在5.0 和60.0之间的数据点。
函数计算后比较 sqrt(value ) < 15.0 查询度量值的平方根小于15.0 的数据点。

3.2 分组聚合

分组聚合,用户可以使用SQL查询中的Group By子句来指定如何进行分组,并使用上面列出的聚合函数,进行聚合运算。分组可以有1)按照tag 列,或者tag列的表达式进行分组,2)按照时间戳上的间隔进行分组,3)前面两种的混合分组。聚合函数请参考TSQL函数参考部分的聚合函数列表。

3.2.1 tag列上的分组聚合

我们在Group By的子句中加入tag列,或者tag列上的表达式,进行分组聚合。

下面的例子中,我们按照datacenter,hostname的前面3字符前缀进行分组,获得每个分组的度量值的最大值,最小值和平均值。

select
  datacenter,
  substr(hostname, 1,3) preHost,
  max(`value`) maxV,
  min(`value`) minV,
  avg(`value`) avgV
from tsdb.`cpu.usage_system`
where `timestamp` >='2016-01-01' and
  `timestamp` <= '2016-01-02'
group by datacenter, prehost

3.2.2 降采样:时间戳间隔上的分组聚合

TSQL引入新函数Tumple,将时间戳维度划分成多个不相交,相同间隔的窗口,用户利用这个函数,可以按照时间维度上的间隔进行分组。

  • Tumble函数
tumble(timestamp_column, interval_expression)

例子tumble(`timestamp`, interval ‘1’ hour):把timestamp划分成1小时为间隔tumble(`timestamp`, interval ‘5’ minute) : 把timestamp划分成5分钟为间隔。

下面例子查询1天之内的时间线数据点,按照时间戳分成1小时的间隔分组,对每一个小时的间隔,计算数据点的最大值,最小值和平均值。

select
  tumble(`timestamp`, interval '1' hour) ts,
  max(`value`) maxV,
  min(`value`) minV,
  avg(`value`) avgV
from tsdb.`cpu.usage_system`
where `timestamp` >='2016-01-01' and
  `timestamp` <= '2016-01-02'
group by  ts

以时间间隔进行分组聚合,在功能上同TSDB的降采样有相似之处,但也有不同:

  • TSDB协议中/api/query的降采样是在单条时间线上进行的聚合运算,而TSQL的时间戳分组聚合可以在多条时间线上进行的聚合运算。
  • TSDB协议中/api/query的降采样使用的聚合函数和多条时间线之间聚合运算的函数是不同的,而TSQL的时间戳聚合同多条时间线之间的聚合使用的是相同的聚合函数。

3.2.3 tag列 + 时间戳间隔上的分组聚合

用户可以同时按照tag列或tag列上的表达式,和时间戳间隔进行分组聚合。下面例子按照datacenter,hostname的前面3字符前缀,加上时间戳的1小时间隔进行分组, 计算每个分组所对应的最大值,最小值和平均值。

select
  datacenter,
  substr(hostname, 1,3) preHost,
  tumble(`timestamp`, interval '1' hour) ts,
  max(`value`) maxV,
  min(`value`) minV,
  avg(`value`) avgV
from tsdb.`cpu.usage_system`
where `timestamp` >='2016-01-01' and
  `timestamp` <= '2016-01-02'
group by datacenter, preHost, ts

3.2.4 查询每条时间线上最新或最久远的数据点

用户可以使用时序聚合函数ts_last()来获取每条时间线上最新的数据,或者使用ts_first()来获取最久远的数据。

select
  hostname,
  ts_last(`value`, `timestamp`) as v_last,
  max(`timestamp`) maxT
from tsdb.`cpu.usage_system`
where `timestamp` >='2016-01-01 00:00:00' and
  `timestamp` <= '2016-01-01 01:00:00'
group by hostname
order by hostname

上面的查询,获取每台主机上在查询时间范围内的最新的数据点的值,以及对应最新数据点对应的时间戳的值。

3.2.5按照tag+时间戳间隔分组,计算每个分组的最新或最久远数据

我们除了可以在group by加入tagk的列,还可以使用tumble()函数获得的时间间隔,来进行分组计算,获取每个分组内最新或最久远的数据点。

select
  hostname, 
  tumble(`timestamp`, interval '10' minute) ts,
  ts_last(`value`, `timestamp`) as v_last,
  max(`timestamp`) maxT
from tsdb.`cpu.usage_system`
where `timestamp` >='2016-01-01 00:00:00' and
  `timestamp` <= '2016-01-01 01:00:00'
group by hostname, ts
order by hostname, ts

3.3 多metric关联join

TSQL查询引擎支持在多条时间线(不同名字的metric)之间进行join。通过join用户可以在多个metric之间进行关联分析。多metric关联Join需要指定join的=条件:

  • 时间戳相同
  • 对应的tag相同

下面的例子把 cpu.usage_systemcpu.usage_idle进行关联分析,查询返回hostname相同并且时间戳相同的两个度量的值。

select t1.`timestamp`, t1.`value` as value1, t2.`value` as value2, t1.hostname
from tsdb.`cpu.usage_system` t1, tsdb.`cpu.usage_idle` t2
where t1.`timestamp` >='2016-01-01' and t1.`timestamp` <= '2016-01-02' 
 and t1.hostname = t2.hostname
 and t1.`timestamp`= t2.`timestamp`
 limit 100

下面的例子,用了相同的join条件,并计算了在tag和时间戳上对齐后,按照hostname进行分组聚合并计算各自度量值的平均值。

select t1.hostname, avg(t1.`value`) as avg_sys, avg(t2.`value`) as avg_idle
from tsdb.`cpu.usage_system` t1, tsdb.`cpu.usage_idle` t2
where t1.`timestamp` >='2016-01-01' and t1.`timestamp` <= '2016-01-02' 
 and t1.hostname = t2.hostname
 and t1.`timestamp`= t2.`timestamp`
group by t1.hostname