TSQL:讓監控分析更簡單更高效

1. 前言

阿里時序時空數據庫TSDB最新推出TSQL,支持標準SQL的語法和函數。用戶使用熟悉的SQL,不只僅查詢更簡單易用,用戶還能夠利用SQL強大的功能,實現更加複雜的計算分析。web

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位。apache

不管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所示,。點擊瞭解阿里雲時序數據庫



本文做者:鳳豪

原文連接

本文爲雲棲社區原創內容,未經容許不得轉載。

相關文章
相關標籤/搜索