zoukankan      html  css  js  c++  java
  • TSQL:让监控分析更简单更高效

    1. 前言

    阿里时序时空数据库TSDB最新推出TSQL,支持标准SQL的语法和函数。用户使用熟悉的SQL,不仅仅查询更简单易用,用户还可以利用SQL强大的功能,实现更加复杂的计算分析。

    2. 为什么需要用SQL做时序查询?

    2.1 SQL拥有广泛用户基础

    SQL作为一个诞生于上世纪70年代的编程语言已经存在几十年了。这是一个相对而言较“古老”的编程语言,但又是一个有着广泛用户基础的语言。
    在跟踪主要编程语言的流行程度的TIOBE index[1]中,SQL在2019年4月份的排名是第8。而如果把排名列在11-20之间的SQL的两个“兄弟”PL/SQL, Transact-SQL也合并进来的话,SQL的流行度应该更高。

    根据stackoverflow网站的调查 [2],SQL在最流行的编程语言榜上排在第4位。

    无论TIOBE index还是stackoverflow的编程语言排行榜,都从一个侧面反映了SQL的广泛用户基础。作为一个查询语言,SQL是用户和数据库系统交互的(直接或间接)主要方式。支持一个拥有广泛用户基础的查询语言,对于推广数据库系统来说,是非常重要的。

    2.2 用户学习成本

    最近几年出现的几个主要面向时序场景的数据库,除了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。时序数据库这样一个新兴的数据库领域,也有可能重复这样的历史。原因很简单,用户学习一个新语言的成本越高,越会阻碍一个系统被推广到大众接受的程度。

    2.3 BI工具生态支持

    时序数据库提供SQL的查询支持,一个很重要的原因是将时序数据库的应用场景扩展到商业分析(BI/Business Analysis),商业决策这样高附加值领域。
    当前几个主要的时序数据库,包括InfluxDB, OpenTSDB和Prometheus,主要侧重于基础性能监控这样的场景,利用Grafana这样的可视化工具,实现监控报警这一类基本功能。另一方面,监控报警还没有充分利用挖掘时序数据的商业价值。进一步的功能,需要充分利用现有SQL生态系统中的商业分析工具,比如Tableau, Qlik,Oracle BI, IBM Cognos等。这些BI工具,往往是以SQL的方式同后端数据库交互。从这个角度来说,时序数据库的SQL支持对于对接BI生态系统中的各种工具,尤为重要。

    2.4 TSQL面向的用户群

    在阿里时序数据库TSDB支持的兼容OpenTSDB查询协议之上推出的TSQL查询引擎,主要是面向以下两类用户:

    • 时序数据库TSDB的新应用开发者 :这类用户往往以前使用关系数据库,因为关系数据库本身处理时序数据的性能和可扩展性的局限,而转而使用TSDB。这些新应用开发者,希望TSDB在提供比关系数据库更好的时序性能和扩展性的同时,能够用他们以前熟悉的查询语言进行应用开发,而不是去学习一个新的查询语言。
    • 数据分析师:这类用户并不开发应用,他们的工作是利用已有的商业分析工具,对时序数据进行进一步的查询分析。他们本身并不直接使用SQL, 但所使用的工具以SQL作为和时序数据库TSDB交互的查询语言。

    3. 现有时序数据库系统SQL查询能力比较

    这里简单对比时序数据库系统中提供SQL查询,或SQL-like查询能力的InfluxDB, TimescaleDB, 阿里云TSDB。

    4. TSQL系统架构

    上图是TSQL的总体架构以及和TSDB引擎和存储之间的协调工作关系。简单来讲,TSQL是一个典型的MPP的SQL分析引擎,通过Connector同TSDB引擎和存储进行数据交换。Connector支持MetaAPI和DataAPI。

    TSQL是在两个Apache开源项目基础上演进开发的:

    • Apache Calcite作为SQL的解析器和计划生成和优化器。
    • Apache Drill提供分布式的SQL执行层。

    Apache Calcite作为一个扩展性好,支持标准SQL语法和语义的SQL计划生成器,已经被很多数据处理相关的开源项目使用[6],包括大数据ETL的Apache Hive, HBase上的SQL解决方案Apache Phoenix, 也有流数据处理框架Apache Fink (阿里的Blink)和Apache Beam等。 TSQL使用Calcite作为SQL计划生成器,可以在兼容标准SQL方面,充分利用开源社区已有的成果。

    4.1 时序数据Schema管理

    InfluxDB, OpenTSDB和Prometheus都采用的是一种Schema-on-write的方式,也就是用户并不需要明确定义metric的schema, 而是将schema的信息隐藏在数据中,在数据写入的时候,同时管理着schema。这样做的好处是更高的灵活性:

    • 在写入数据的时候,用户不需要事先必须用Create Table DDL来创建table;
    • 在时序数据tag set出现变化的时候,用户不需要事先用Alter Table来修改table的schema。
      TimeScaleDB从PG上扩展而来,所以是采用的是严格的Schema的管理方式。在使用灵活性方面,不如上面其他3个时序数据库。

    Calcite作为一个SQL计划生成器,很适合时序数据库这样的比较松散的Schema管理方式。 Calcite的Schema Adapter,可以支持

    1. 动态的Schema 发现,
    2. 任意一个数据集,只要实现Schema管理中的接口API, 就可以在计划解析生成阶段被当成一个Table来处理。

    TSQL在Calcite的Schema Adapter基础上,利用TSDB引擎中新增加的MetaAPI,来完成SQL计划解析和生成。这免去了用户必须事先在一个集中式的catalog中预先定义Table DDL等繁琐工作,给用户带来了很多的灵活性。

    4.2 时序数据查询执行

    TSQL的执行层,利用了Apache Drill的runtime execution。Drill的runtime execution,具备以下特点

    • 利用off-heap内存作为计算内存,减少Java heap内存GC所带来的延迟问题
    • 基于Columnar格式的ValueVector (Apache Arrow的前身),提升查询执行效率
    • 动态代码生成和编译
      UDF支持

    5. TSQL时序查询功能

    我们以一个基础性能监控场景来举例说明TSQL能完成的时序查询功能。利用一个时序数据库业界公开的时序性能Benchmark[5] 生成的模拟数据,按照DevOps这样的场景,产生了cpu相关的10不同的metric。每个metric对应了机房(datecenter),主机(hostname),rack等标签下所采集的服务器cpu相关的指标数据。

    5.1 元数据查询

    可以用下面的方式查询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'命令的部分结果:

    5.2 时序数据简单查询

    用下面的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.3 时序降精度,聚合运算

    如果我们要计算两小时之内,每台主机上每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获得数据后,再进行后计算,来满足业务需要。在自己的应用程序中进行后计算,往往需要付出很大的应用开发代价。

    5.4 聚合后计算,过滤,排序

    下面的例子,计算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

    5.5 任意复杂的条件表达式

    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为"定制化监控报警"的功能实现,提供了有力的技术保障。

    5.6 多个metric之间join

    这个查询,把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, 多值模型也无法解决问题。

    5.7 分组聚合后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

    5.8 UDF扩展功能

    使用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;
            }
        }

    6 TSQL可视化查询

    阿里云TSDB已经提供了TSQL可视化交互式开发功能,通过web页面可以方便的进行TSQL的测试和开发,如下图Demo所示,。点击了解阿里云时序数据库

    image

    本文作者:凤豪

    原文链接

    本文为云栖社区原创内容,未经允许不得转载。

  • 相关阅读:
    【Uvalive4960】 Sensor network (苗条树,进化版)
    【UVA 1151】 Buy or Build (有某些特别的东东的最小生成树)
    【UVA 1395】 Slim Span (苗条树)
    【UVA 10600】 ACM Contest and Blackout(最小生成树和次小生成树)
    【UVA 10369】 Arctic Network (最小生成树)
    【UVA 10816】 Travel in Desert (最小瓶颈树+最短路)
    【UVA 11183】 Teen Girl Squad (定根MDST)
    【UVA 11865】 Stream My Contest (二分+MDST最小树形图)
    【UVA 11354】 Bond (最小瓶颈生成树、树上倍增)
    【LA 5713 】 Qin Shi Huang's National Road System (MST)
  • 原文地址:https://www.cnblogs.com/zhaowei121/p/11328243.html
Copyright © 2011-2022 走看看