阿里時序時空數據庫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查詢引擎,主要是面向如下兩類用戶:架構
這裏簡單對比時序數據庫系統中提供SQL查詢,或SQL-like查詢能力的InfluxDB, TimescaleDB, 阿里雲TSDB。框架
上圖是TSQL的整體架構以及和TSDB引擎和存儲之間的協調工做關係。簡單來說,TSQL是一個典型的MPP的SQL分析引擎,經過Connector同TSDB引擎和存儲進行數據交換。Connector支持MetaAPI和DataAPI。編程語言
TSQL是在兩個Apache開源項目基礎上演進開發的:分佈式
Apache Calcite做爲一個擴展性好,支持標準SQL語法和語義的SQL計劃生成器,已經被不少數據處理相關的開源項目使用[6],包括大數據ETL的Apache Hive, HBase上的SQL解決方案Apache Phoenix, 也有流數據處理框架Apache Fink (阿里的Blink)和Apache Beam等。 TSQL使用Calcite做爲SQL計劃生成器,能夠在兼容標準SQL方面,充分利用開源社區已有的成果。
InfluxDB, OpenTSDB和Prometheus都採用的是一種Schema-on-write的方式,也就是用戶並不須要明肯定義metric的schema, 而是將schema的信息隱藏在數據中,在數據寫入的時候,同時管理着schema。這樣作的好處是更高的靈活性:
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所示,。點擊瞭解阿里雲時序數據庫
本文爲雲棲社區原創內容,未經容許不得轉載。