阿里时序时空数据库TSDB最新推出TSQL,支持标准SQL的语法和函数。用户使用熟悉的SQL,不只仅查询更简单易用,用户还能够利用SQL强大的功能,实现更加复杂的计算分析。web
SQL做为一个诞生于上世纪70年代的编程语言已经存在几十年了。这是一个相对而言较“古老”的编程语言,但又是一个有着普遍用户基础的语言。
在跟踪主要编程语言的流行程度的TIOBE index[1]中,SQL在2019年4月份的排名是第8。而若是把排名列在11-20之间的SQL的两个“兄弟”PL/SQL, Transact-SQL也合并进来的话,SQL的流行度应该更高。数据库
根据stackoverflow网站的调查 [2],SQL在最流行的编程语言榜上排在第4位。apache
不管TIOBE index仍是stackoverflow的编程语言排行榜,都从一个侧面反映了SQL的普遍用户基础。做为一个查询语言,SQL是用户和数据库系统交互的(直接或间接)主要方式。支持一个拥有普遍用户基础的查询语言,对于推广数据库系统来讲,是很是重要的。编程
最近几年出现的几个主要面向时序场景的数据库,除了TimescaleDB是在Postgres基础上因此支持PG生态包括SQL语言支持,其余几个好比InfluxDB, OpenTSDB, Prometheus都有各自不一样的查询语言和接口:InfluxDB有InfluxQL,OpenTSDB有本身的Restful API, 而Prometheus有PromQL。每个系统均可以声称本身的语言是独一无二的,更适合时序查询这样的场景;但不能否认的事实是用户须要去花时间去学习一种新的语言,而且若是这个语言为了功能完善,还在不断演进中,这样的学习成本对用户来讲,尤为显得高了。
举个例子,InfluxDB的InfluxQL并不支持Join,Subqueries, 以及SQL中很常见的UDF等功能,这意味着用户并不能在不一样数据之间进行关联分析计算,也不能在系统函数基础上进行扩展开发。InfluxDB设计者在听到社区的意见后,作了一个颇有“创意”的事情:在新版本里支持Join,UDF等功能,但并非让InfluxQL变得更加接近于SQL,而是在一个全新的Flux(一个新的functional scripting language)里支持 [3]。用户想要作InfluxQL不能作的事情,那就再来学习一个新语言吧。
一个颇有意思的事情,10多年前开始出现的NoSQL系统,好比MapReduce/Hadoop, BigTable,Casandra,HBase等,一开始也是以各自不一样的查询语言出现的。在经历了多年用户推广以后,NoSQL开始拥抱SQL,变成了NotOnlySQL或者NewSQL。时序数据库这样一个新兴的数据库领域,也有可能重复这样的历史。缘由很简单,用户学习一个新语言的成本越高,越会阻碍一个系统被推广到大众接受的程度。后端
时序数据库提供SQL的查询支持,一个很重要的缘由是将时序数据库的应用场景扩展到商业分析(BI/Business Analysis),商业决策这样高附加值领域。
当前几个主要的时序数据库,包括InfluxDB, OpenTSDB和Prometheus,主要侧重于基础性能监控这样的场景,利用Grafana这样的可视化工具,实现监控报警这一类基本功能。另外一方面,监控报警尚未充分利用挖掘时序数据的商业价值。进一步的功能,须要充分利用现有SQL生态系统中的商业分析工具,好比Tableau, Qlik,Oracle BI, IBM Cognos等。这些BI工具,每每是以SQL的方式同后端数据库交互。从这个角度来讲,时序数据库的SQL支持对于对接BI生态系统中的各类工具,尤其重要。服务器
在阿里时序数据库TSDB支持的兼容OpenTSDB查询协议之上推出的TSQL查询引擎,主要是面向如下两类用户:架构
**- 时序数据库TSDB的新应用开发者
**:这类用户每每之前使用关系数据库,由于关系数据库自己处理时序数据的性能和可扩展性的局限,而转而使用TSDB。这些新应用开发者,但愿TSDB在提供比关系数据库更好的时序性能和扩展性的同时,可以用他们之前熟悉的查询语言进行应用开发,而不是去学习一个新的查询语言。
**- 数据分析师:
**这类用户并不开发应用,他们的工做是利用已有的商业分析工具,对时序数据进行进一步的查询分析。他们自己并不直接使用SQL, 但所使用的工具以SQL做为和时序数据库TSDB交互的查询语言。框架
这里简单对比时序数据库系统中提供SQL查询,或SQL-like查询能力的InfluxDB, TimescaleDB, 阿里云TSDB。编程语言
上图是TSQL的整体架构以及和TSDB引擎和存储之间的协调工做关系。简单来说,TSQL是一个典型的MPP的SQL分析引擎,经过Connector同TSDB引擎和存储进行数据交换。Connector支持MetaAPI和DataAPI。分布式
TSQL是在两个Apache开源项目基础上演进开发的:
InfluxDB, OpenTSDB和Prometheus都采用的是一种Schema-on-write的方式,也就是用户并不须要明肯定义metric的schema, 而是将schema的信息隐藏在数据中,在数据写入的时候,同时管理着schema。这样作的好处是更高的灵活性:
TimeScaleDB从PG上扩展而来,因此是采用的是严格的Schema的管理方式。在使用灵活性方面,不如上面其余3个时序数据库。
Calcite做为一个SQL计划生成器,很适合时序数据库这样的比较松散的Schema管理方式。 Calcite的Schema Adapter,能够支持
TSQL在Calcite的Schema Adapter基础上,利用TSDB引擎中新增长的MetaAPI,来完成SQL计划解析和生成。这免去了用户必须事先在一个集中式的catalog中预先定义Table DDL等繁琐工做,给用户带来了不少的灵活性。
TSQL的执行层,利用了Apache Drill的runtime execution。Drill的runtime execution,具有如下特色
咱们以一个基础性能监控场景来举例说明TSQL能完成的时序查询功能。利用一个时序数据库业界公开的时序性能Benchmark[5] 生成的模拟数据,按照DevOps这样的场景,产生了cpu相关的10不一样的metric。每一个metric对应了机房(datecenter),主机(hostname),rack等标签下所采集的服务器cpu相关的指标数据。
能够用下面的方式查询TSDB中全部的metric/table
SHOW TABLES FROM tsdb
若是咱们但愿列出全部以cpu为前缀的metric/table,能够在上面的查询基础之上添加附带过滤条件.
show TABLES from tsdb where TABLE_NAME like 'cpu%'
下图给出了命令的部分输出:
在得到metric/table 名字后,咱们能够进一步用SQL中的'DESCRIBE'命令来查询这个metric/table的schema信息
describe tsdb.`cpu.usage_user`
下图显示了上面的'describe'命令的部分结果:
用下面的SQL查询能够得到指定时间段内的'cpu.usage_user'的指标值,时间戳,以及对应的标签值。
select * from tsdb.`cpu.usage_user` where `timestamp` between '2019-05-01 16:00:00' and '2019-05-01 18:00:00'
这里, 将被转换成 metric/table下全部的列,包括指标值,时间戳,全部的标签列。能够以具体的列名的一个列表来代替。
做为对比,若是把上面的查询转化成OpenTSDB协议来查询,相对应的查询以下:
{ "start": "1556726400000", "end": "1556733600000", "queries": [ { "aggregator": "none", "metric": "cpu.usage_user", "rate": null, "downsample": null, "filters": [] } ] }
能够在时间戳的过滤条件基础上,增长指标列上的条件。下面的查询,列出指定时间段内,3台主机上的指标值,而且使用limit, 把查询结果限制在100行。
select * from tsdb.`cpu.usage_user` where `timestamp` between '2019-05-01 16:00:00' and '2019-05-01 18:00:00' and hostname in ('host_1', 'host_5', 'host_10') limit 100
能够在查询中使用标准SQL中丰富的数值计算函数,字符串函数或时间戳函数。下面的SQL,咱们分别使用了数值运算函数sqrt, 时间戳函数extract 和字符串lower。
若是咱们要计算两小时以内,每台主机上每5分钟的指标cpu.usage_user的最大值,最小值,以及数据采样点的个数。这样的查询,表明了在时间维度上的降精度,而且在标签hostname上进行的聚合运算。用TSQL来表示这样的查询:
select hostname, tumble(`timestamp`, interval '5' minute) ts, max(`value`) maxV, min(`value`) minV, count(`value`) cnt from tsdb.`cpu.usage_user` where `timestamp` between 1556726400000 and 1556733600000 and hostname in ('host_8','host_5','host_6') group by hostname, ts
若是用OpenTSDB的协议来查询:
{ "start": "1556726400000", "end": "1556733600000", "queries": [ { "aggregator": "max", "metric": "cpu.usage_user", "downsample": "5m-max", "tags":{ "hostname":"host_8|host_5|host_6" } }, { "aggregator": "min", "metric": "cpu.usage_user", "downsample": "5m-min", "tags":{ "hostname":"host_8|host_5|host_6" } }, { "aggregator": "sum", "metric": "cpu.usage_user", "rate": null, "downsample": "5m-count", "tags":{ "hostname":"host_8|host_5|host_6" } } ] }
能够看到,相比较原来Restful API的查询,TSQL可以用更简洁的方式来表示相同的查询语义;而且,若是用户原本就熟悉SQL的使用方法,节省用户去学习Restfule API里JSON各个字段的含义。从下降用户学习成本,增长易用性这个角度,TSQL带来了较明显的价值。
TSQL不只仅带来查询简洁,用户易用的优势,而且,更重要的是,用TSQL可以表达Restful API里不能直接表达的查询语义。在TSDB引入TSQL以前,若是用户须要进行这样的查询计算,则用户必须经过本身的应用程序,在Restful API得到数据后,再进行后计算,来知足业务须要。在本身的应用程序中进行后计算,每每须要付出很大的应用开发代价。
下面的例子,计算2个小时内,3台机器上每5分钟内,cpu.usage_user指标值的最大值和最小值的差别超过10.0的时段和hostname, 并按照差别值从大到小排序:
在上面的例子中个,在得到最大值和最小值后,进一步计算二者的差别值,并根据差别值进行过滤和排序。这样的聚合后计算处理,没法用OpenTSDB的查询协议表示;用户若是要表达这样的语义,就必须在应用程序中计算。
select hostname, tumble(`timestamp`, interval '5' minute) ts, max(`value`) - min(`value`) as diffV from tsdb.`cpu.usage_user` where `timestamp` between '2019-05-01 16:00:00' and '2019-05-01 18:00:00' and hostname in ('host_1', 'host_5', 'host_10') group by hostname, ts HAVING diffV > 10.0 order by diffV DESC
TSDB的Restful API对于只提供有限的几种filter, 而并不支持任意filter经过AND/OR的组合。好比下面的例子,是一个TSQL业务中使用的查询。其中WHERE条件部分是并不能用Restful API来表示的,由于Restful下的filters是只有AND, 而OR只有在相同tag上经过'value1|value2|vale3'这样的形式来表达。
where ( (obj_id='ems30_NA62_183249003' and obj_type='ems30_NA62_20204' and room='ems30_NA62_C-T01.NA62' and building='ems30_NA62_C') or (obj_id='ems30_NA62_183249746' and obj_type='ems30_NA62_20204' and room='ems30_NA62_C-T01.NA62' and building='ems30_NA62_C') or (obj_id='ems30_NA62_183246962' and obj_type='ems30_NA62_20204' and room='ems30_NA62_C-T01.NA62' and building='ems30_NA62_C') or (obj_id='ems30_NA62_183248143' and obj_type='ems30_NA62_20204' and room='ems30_NA62_C-T01.NA62' and building='ems30_NA62_C') or (obj_id='ems30_NA62_183249191' and obj_type='ems30_NA62_20204' and room='ems30_NA62_C-T01.NA62' and building='ems30_NA62_C') or (obj_id='ems30_NA62_183249964' and obj_type='ems30_NA62_20204' and room='ems30_NA62_C-T01.NA62' and building='ems30_NA62_C') or (obj_id='ems30_NA62_183247148' and obj_type='ems30_NA62_20204' and room='ems30_NA62_C-T01.NA62' and building='ems30_NA62_C') ) and `timestamp` between '2019-04-25 18:20:21' and '2019-04-25 18:20:31' ...
支持任意组合的AND/OR的条件表达式,对于应用开发是颇有意义的。在集团基础监控业务(raptor-pro)中,一个突出的亮点是“定制化监控报警”:容许业务方的用户来定制查询条件,而且查询条件能够是任意的AND/OR组合。TSQL为"定制化监控报警"的功能实现,提供了有力的技术保障。
这个查询,把cpu.usage_system和cpu.usage_idle在hostname和timestamp上作等值join, 而后计算每5分钟两个度量值之和的sum。
select t1.hostname, tumble(t1.`timestamp`, interval '5' minute ) ts, sum(t1.`value` + t2.`value`) as sumV from tsdb.`cpu.usage_system` t1, tsdb.`cpu.usage_idle` t2 where t1.`timestamp` >='2019-05-01' and t1.`timestamp` <= '2019-05-01 01:00:00' and t1.hostname = t2.hostname and t1.`timestamp`= t2.`timestamp` group by t1.hostname, ts
上面的查询,若是咱们采用TSDB的多值模型,把cpu.usage_system和cpu.usage_idle处理成一个metric的不一样的field, 则不须要join就能够完成。但若是咱们须要在分组聚合后的结果上再作join, 多值模型也没法解决问题。
下面的查询,分别对cpu.usage_system和cpu.usage_idel按照5分钟计算聚合函数sum(), 再经过join, 对齐,计算相对应的比例。而且,每一个子查询的Where条件,除了包括在tag上和时间戳上的条件,还包括值上的过滤条件。
相似这样的查询,是没法直接在TSDB的RestAPI来实现的;用户只能在本身的应用程序中实现,增长了应用开发成本。
select f0.hostname, f0.ts, f0.sumV / f1.sumV as resultValue from ( select hostname, tumble(`timestamp`, interval '5' minute) ts, sum(`value`) as sumV from tsdb.`cpu.usage_system` where hostname in ('host_0', 'host_5', 'host_10') and `timestamp` between '2019-05-01 00:00:00' and '2019-05-01 01:00:00' and `value`<=50 group by hostname, ts ) as f1 join ( select hostname, tumble(`timestamp`, interval '5' minute ) ts, sum(`value`) as sumV from tsdb.`cpu.usage_idle` where hostname in ('host_0', 'host_5', 'host_10') and `timestamp` between '2019-05-01 00:00:00' and '2019-05-01 01:00:00' and `value`<=30 group by hostname, ts ) as f0 on f1.hostname = f0.hostname and f1.ts = f0.ts
使用UDF来扩展功能,对于时序数据库这样聚焦特定领域的数据库来讲,是很是必要的,由于每每SQL标准中定义的函数,并不能彻底知足须要。TSQL有一个完善的UDF的体系,用户只要按照约定的接口,用Java语义就能够实现扩展。好比,咱们在TSQL中引入的把时间戳分割成不重合的窗口的函数tumble,其实现就是由下面不到15行代码完成。
用户能够用Java实现不一样的scalar UDF或者aggregate UDF, 并把编译后的jar加入到TSQL的系统类库目录,就能够自行扩展TSQL的查询计算功能了。
@FunctionTemplate(name = "tumble", scope = FunctionTemplate.FunctionScope.SIMPLE, nulls = FunctionTemplate.NullHandling.NULL_IF_NULL) public static class Tumble implements DrillSimpleFunc { @Param TimeStampHolder timeStamp; @Param IntervalDayHolder interval; @Output TimeStampHolder out; @Override public void setup() { } @Override public void eval() { long intervalMs = interval.days * org.apache.drill.exec.vector.DateUtilities.daysToStandardMillis + interval.milliseconds; out.value = timeStamp.value - timeStamp.value % intervalMs; } }
阿里云TSDB已经提供了TSQL可视化交互式开发功能,经过web页面能够方便的进行TSQL的测试和开发,以下图Demo所示。
原文连接 本文为云栖社区原创内容,未经容许不得转载。