天天看點

六大特點全面更新!TDengine 3.0 全新計算查詢引擎實作思路詳解

在 8 月 13 日的 TDengine 開發者大會上,TDengine 計算引擎架構師廖浩均帶來題為《TDengine 3.0——全新計算查詢引擎的設計》的主題演講,詳細闡述了 TDengine 3.0 計算查詢引擎技術的優化與更新。本文根據此演講整理而成。

3.0 中的查詢引擎在 2.0 版本的基礎上進行了重寫,在承襲 2.0 查詢引擎既有優勢和技術特性的基礎上,在工程實作和整體架構設計上有了極大的改善和提升。

總體來講,TDengine 3.0 大幅增強了對 SQL 語義的支援、完善了 SQL 查詢文法;強化了整體執行架構及 SQL 查詢的排程能力;提供更好的執行任務隔離機制,對于錯誤具備更好的容忍度;提供支援存算分離架構的能力。它主要包含以下幾個方面的特點:

  1. 支援标準 SQL 查詢文法
  • 全新的 SQL 解析器、計劃生成及優化器,引入僞列 (_wstart/_wend/_qstart/_qend/_rowts) 等文法概念和新的(分組)關鍵詞,全面增強查詢文法。
  • 消除 2.x 版本中對時序資料查詢施加的諸多限制,可以像使用關系對象資料庫查詢引擎一樣使用 SQL 查詢文法。
  • 支援标簽/普通列的運算,标量函數和矢量函數的嵌套、任意字段(列)的分組/排序/聚合、無限制的多級子查詢。
  1. 支援存算分離架構
  • 支援存算分離的體系架構,支援叢集内計算節點動态、快速、彈性部署。
  • 查詢引擎能夠将查詢按需排程到計算節點,有效降低存儲節點(Vnode)計算資源的壓力。
  1. 原生的流批一體化查詢處理
  • TDengine 3.0 原生支援流計算和互動式查詢。流計算引擎也使用 SQL 語言作為互動途徑,是以,流計算引擎直接使用查詢架構的 SQL 解析處理、執行計劃生成、查詢執行算子、使用者應用互動等諸多方面的能力。
  1. 更優秀的魯棒性和韌性設計
  • 為避免使用者錯誤導緻的主服務當機,使用者定義函數的執行服務(UDFD)與 TDengine 的主程序進行隔離,UDFD 可以在崩潰以後自動被重新拉起。
  • 對于 SQL 查詢轉化為排程器的查詢任務(Job),Job 在被分解以後會按層級排程執行,并且同層次任務互相獨立執行,單個任務失敗以後可以重新拉起進行處理。
  • 對于不同分層的任務(Task),失敗任務可以從下遊任務的結果緩存(Sink Node)中再次請求資料并嘗試重新執行。
  1. 更好的執行可觀測性
  • 将查詢計劃、執行開銷、查詢中 Task 的動态執行狀态等相關資訊通過指令行互動界面(CLI)呈現給使用者,助力使用者定位查詢執行的瓶頸,并進行針對性的優化處理。
  • 提供 Explain 指令,使用者可以通過 Explain 獲得查詢執行的計劃、各算子執行開銷等資訊,為使用者層面 SQL 改寫和優化性能提供支援。
  • 通過心跳資訊,将服務端執行狀态推送到排程器,并回報給使用者,讓使用者更好确定查詢執行的狀态。
  1. 支援資料集快照查詢
  • 全面内置版本化的查詢支援能力,可以為事件驅動流計算引擎提供指定版本和時間範圍的曆史資料追溯查詢。
  • 為雙(多)活叢集提供資料同步版本化讀取能力。

查詢引擎架構設計

六大特點全面更新!TDengine 3.0 全新計算查詢引擎實作思路詳解

上圖是 TDengine 3.0 的查詢引擎架構,左邊的 Query Wrapper 運作在 Vnode 和 Qnode 上,它裡面包含了一個執行器,其中的 Index 子產品是處理 Vnode 中存儲的标簽資料的索引。執行器本身并不處理與排程器的互動行為,Query Wrapper 負責處理執行 Task 動作并将結果緩存在 Sink Node;此外,執行器會調用 function 子產品和 ScalarFunc 子產品進行計算處理,并在必要的情況下通過 function 子產品調用 UDF 服務。

中間的子產品是由 Query Wrapper 和 libtaos.so 所共享的,裡面包含的是 function(系統中其他所有函數的定義子產品)和 ScalarFunc(标量函數以及過濾的子產品)。需要注意的是,在 function 子產品中還包含了一個 UDFD 的計算子產品,它是使用者定義程式的執行服務端,運作在一個獨立的程序空間。

最右邊的子產品是運作在使用者程序空間中的驅動 libtaos.so,Driver 是 libtaos.so 裡的一個驅動,它負責串聯各個子產品來完成查詢的操作,Parser 用來調用分詞器與文法分析器進行文法分析,生成文法樹(AST),Catalog 負責從 Mnode 和 Vnode 中擷取各種中繼資料,Planner 負責将 AST 和 中繼資料資訊轉化為邏輯執行計劃、實體執行計劃、并進行執行計劃重寫,最後生成分層查詢計劃,Command 負責執行本地化查詢操作。

相比于 2.0,3.0 在子產品化和工程化上做了較多的優化,主要可以歸結為以下幾方面:

  • 用戶端不參與計算,計算過程在服務端完成
  • UDF 在獨立的程序空間中運作,與主服務程序隔離
  • Qnode-aware 的自适應查詢任務排程政策
  • Index-aware 的标簽過濾機制
  • 節點間/算子間采用标準的列格式(columnar data)資料進行資料傳遞
  • 進行計算的節點會通過心跳将查詢執行資訊傳回給排程器
  • 重寫查詢架構,形成了高内聚、低耦合的架構子產品

時序資料查詢處理流程

六大特點全面更新!TDengine 3.0 全新計算查詢引擎實作思路詳解

首先,Parser 會把圖上的 SQL 語句轉成一個抽象文法樹,之後會依托于 Catalog 節點拉取相應的中繼資料,然後将資訊傳遞到 planner,生成如圖上所示的邏輯計劃。之後在邏輯計劃層面會生成四個 node,自底向上分别是 Scan、Partition、Window、Project,它們分别對應 SQL 語句裡不同的幾個關鍵詞。

六大特點全面更新!TDengine 3.0 全新計算查詢引擎實作思路詳解

邏輯計劃傳遞下去,最後會在 planner 中生成一個實體計劃。上圖所示的紅色虛線以下部分(Partial Agg)是對實體計劃的執行,包括 VgID:#1 和VgTD:#2 兩個子任務;虛線上層是一個Global Merge,是最後的一個聚合階段,Partial 聚合結果經過 merge sort 後再次進行全局聚合,形成結果以後送到計算節點本地的 sink node 進行緩存,等待 scheduler 來拉取最後的計算結果。

在之前邏輯計劃中的 Logic partition 在實體計劃中卻看不到了,其實 Partition 分組邏輯是被下沉到了 TableScan 完成,這是一個優化操作。按照标簽分組機制,同一個表中資料一定同一分組,我們在 TableScan Node 中建立表分組的映射關系即可,再将資料塊打上 GroupID 就完成了分組操作,不需要對每一條資料進行掃描。

實體計劃的具體實作在 TDengine 系統執行的日志裡面可以看到,感興趣的同學可以去看看,系統中的實體計劃采用 JSON 形式的文本呈現。

在實際操作中,實體計劃最終會傳遞到排程器中執行,排程器工作的元素就是 Task 和 Job,每個SQL 查詢是一個 Job,每個 Job 由若幹個 Task 構成,每個 Task 包含了其執行的節點資訊、任務 ID、需要執行的實體計劃等資訊,不同的 Task 會形成一個層級化的樹形結構。

實體計劃傳遞到排程器後,虛線下面的兩個 Partial Agg 會被轉化成 Subplan #1(level 1) 和 Subplan #2(level 1),這兩個執行計劃會分别發送到不同的 Vnode 進行執行。虛線上層也是一個 Task [Subplan #0(level 0)],等下層兩個 Task 執行完成或至少傳回第一條記錄時,排程器就會拉起上一層 level 0 的 Task 進行執行,它是層級化的執行方式。

六大特點全面更新!TDengine 3.0 全新計算查詢引擎實作思路詳解

時序資料查詢處理的整個流程

時序資料查詢處理的整體流程如上圖所示,其中計算節點的選擇主要有以下幾點政策:

  • 部分聚合計算的計算節點由于聚合計算下推,要求在儲存有資料的 Vnode 上進行,以減小資料在叢集中傳輸帶來的開銷;
  • 對于全局聚合,可以在叢集中所有可用節點中任意選取,為了減小資料在叢集中傳輸的開銷,一般會選擇進行局部聚合(Partial Agg) 的節點進行全局聚合;
  • 如果在叢集中存在 Qnode(計算節點),排程器會優先将後續階段的計算排程到計算節點上進行計算。

時序資料查詢優化政策

排序消除

排序是查詢過程中,I/O 和 CPU 開銷都非常高的操作。如果查詢結果要求按照 timestamp 排序(升序或降序)輸出,由于時序資料在 TDengine 中就是按照時間序列存儲,是以排序的操作可用直接消除,隻需要将 TableScan 指定為升序/降序掃描傳回結果即可。

排序優化

六大特點全面更新!TDengine 3.0 全新計算查詢引擎實作思路詳解

操作邏輯如上圖所示。對于分布式的結果排序輸出,排序操作充分利用時序資料有序性,如果我們使用歸并排序替代完全排序,就能避免在标準外存排序過程中觸發的 IO 操作。這一操作主要在超級表查詢的場景中使用較多。

資料替代

從這一條優化政策開始,需要給大家補充一下關于 SMA 的背景知識。SMA 是 Small Materialized Aggregates 的簡稱。對于每個落盤的資料塊(Block)都會生成一個對應的 SMA 資訊,其中包含了每列資料的最大值、最小值、NULL 數量資訊。這些資訊針對數值型列都存在,必要時可以替代資料參與計算。相對于具體的資料來說,SMA 占據的磁盤空間非常小,是以能夠極大地提升某些種類的查詢性能。

六大特點全面更新!TDengine 3.0 全新計算查詢引擎實作思路詳解

基于 SMA 的優化政策之一就是資料替代。針對每個函數,TDengine 定義了其需要的資料範圍,上表是一些聚合函數的資料計算需求清單。在 SQL 解析階段,如果确認其最終的資料需求是 Small Materialized Aggregates (SMA),最終隻會讀取 SMA 進行計算。但并非任何情況下都可以使用 SMA,對于所有标量函數,一旦在查詢請求裡面出現,SMA 是不參與到計算裡面來的。

在使用 SMA 時,查詢優化器會告訴執行節點,隻需要去讀取每個 block 的 SMA 資訊就行,在整個查詢過程中,執行器不會再去讀取任何一條具體的資料,是以它的流程非常快。

資料掃描優化

TDengine 中定義了資料順序敏感型函數,在 SQL 語句中使用該類型的函數會觸發優化器使用特定的資料檔案掃描政策。在 SQL 語句 ​

​SELECT last(ts),count(*) FROM foo_table_name​

​ 中,Last 函數傳回最後一個非 NULL 值,是以逆序掃描能夠更快獲得結果。而 count 函數對于掃描的順序并不敏感,是以,優化器會指令 TableScanNode 采用逆序掃描政策。

動态裁剪 Block

六大特點全面更新!TDengine 3.0 全新計算查詢引擎實作思路詳解

對于某些查詢函數,例如:last/first/top/bottom 查詢,可以使用中間結果來動态裁剪讀取的 block 的資訊。

在執行過程之中,根據中間計算的結果及目前結果可以确定,下一個需要掃描的 block 是否需要真正地讀取。依然以 last 為例 ​

​SELECT last(ts) FROM foo_table_name​

​,在每次需要讀取下一個 Database 時,首先使用 ts=100 過濾每個資料塊,如果資料塊包含的時間範圍晚于 ts=100, 才會讀取該資料塊資料并進行計算,大于 ts=100 的就自動跳過,這就實作了動态裁剪 Block 的功能。

基于 SMA 的預過濾

在進行 Last 查詢時,SMA 會被用來做預過濾,其中儲存了與之對應的資料塊的四項統計資訊:最大值、最小值、和 NULL 的數量。所有的針對資料的過濾條件首先應用在 SMA 之上,SMA 滿足過濾條件以後,再讀取資料塊,然後再次進行針對資料的過濾。

以 ​

​SELECT last(ts),count(*) FROM foo_table_name WHERE k > 20​

​ 為例,這裡面有一個過濾條件是 K 大于 20,如果其中存儲着關于 K 那一列的最大值、最小值資訊,就可以以此為條件進行過濾,如果其中的 MAX 值都小于 20,那麼這個 block 也會在 TableScanNode 裡面讀取真實資料之前就丢棄。

Interval SMA

時序資料庫​(Time Series Database​,TSDB)一個重要的應用場景就是為看闆(Dash Board)提供定時的查詢(Standing Query, 應用或程式按照固定頻率發出的查詢)支援,看闆應用以固定的頻率向應用及時序資料庫發出查詢執行,并在可接受的時間範圍内要求獲得查詢結果。

針對 Standing Query 的執行需求,為了提升響應速度,節約重複計算帶來的資源開銷。在 TDengine 3.0 中提供了使用流計算引擎的異步 interval SMA(将針對時間視窗的聚合查詢轉化為投影查詢,因為計算結果已經通過流計算引擎計算完成并寫回到 TDengine)。

事實上,上述優化并不是 3.0 查詢計算引擎裡包含的所有優化政策,隻是涉及到了幾個較為經典的查詢場景。關于查詢引擎的優化是非常瑣碎的,有些優化政策隻針對一種場景,甚至如果改寫一下語句順序可能就不生效了。如果大家還想了解更多的查詢引擎優化政策,可以去 GitHub 上查閱 3.0 的代碼,或者直接下載下傳進行體驗。

結語

  • 标量計算庫采用 SIMD 進行優化:采用 SIMD 指令加速标量計算的執行速度
  • 支援多種腳本語言的 UDF/UDAF:3.0 現在支援 C 語言定義的 UDF/UDAF, 後續将支援腳本語言定義的 UDF/UDAF,提供更便捷的使用體驗
  • 支援高精度數值類型和二進制類型資料:高精度 Decimal 類型和 binary(1MB)類型資料的存儲、讀取和查詢
  • 完善查詢優化器:查詢優化器針對更多的查詢場景和用例提供高效率的優化,降低 SQL 執行的複雜程度
  • 支援多源資料的聯邦查詢:支援使用外部資料源的聯邦查詢,通過定義連接配接器,将其他的資料平台作為查詢的基礎資料源
  • 提供更豐富的時序相關計算及分析函數和 SQL 查詢文法:部分聚合函數将支援 CASE/WHEN、CTE 文法、SQL Hint,以及針對資料分析應用的需求,并提供更多的内置 SQL 函數