揭秘TDSQL-A分布式執(zhí)行框架:解放OLAP關聯(lián)分析查詢性能瓶頸

來源: 騰訊云數(shù)據(jù)庫
作者:騰訊云數(shù)據(jù)庫
時間:2021-08-05
16291
在“國產(chǎn)數(shù)據(jù)庫硬核技術沙龍-TDSQL-A技術揭秘”系列分享中,5位騰訊云技術大咖分別從整體技術架構、列式存儲及相關執(zhí)行優(yōu)化、集群數(shù)據(jù)交互總線、分布式執(zhí)行框架設計及優(yōu)化策略、以及向量化執(zhí)行引擎等多方面對TDSQL-A進行了深入解讀。

在“國產(chǎn)數(shù)據(jù)庫硬核技術沙龍-TDSQL-A技術揭秘”系列分享中,5位騰訊云技術大咖分別從整體技術架構、列式存儲及相關執(zhí)行優(yōu)化、集群數(shù)據(jù)交互總線、分布式執(zhí)行框架設計及優(yōu)化策略、以及向量化執(zhí)行引擎等多方面對TDSQL-A進行了深入解讀。

本期帶來了系列分享中騰訊云數(shù)據(jù)庫高級工程師張倩老師主題為“TDSQL-A分布式執(zhí)行框架設計及優(yōu)化策略”的分享的文字版。沒有聽直播的小伙伴,可要認真做筆記啦!

作為領先的分析型數(shù)據(jù)庫,TDSQL-A是騰訊首款分布式分析型數(shù)據(jù)庫,采用全并行無共享架構,具有自研列式存儲引擎,支持行列混合存儲,適應于海量OLAP關聯(lián)分析查詢場景。它能夠支持2000臺物理服務器以上的集群規(guī)模,存儲容量能達到單數(shù)據(jù)庫實例百P級。

一、執(zhí)行框架總體設計

1.1 TDSQL-A架構

首先介紹TDSQL-A的總體架構,包括上層的協(xié)調(diào)節(jié)點CN、GTM事務管理器、中間的數(shù)據(jù)交互總線FN、以及下方的數(shù)據(jù)節(jié)點DN。主要介紹的是協(xié)調(diào)節(jié)點CN和數(shù)據(jù)節(jié)點DN的相關內(nèi)容,包括用戶的查詢怎么在CN和DN上執(zhí)行、最后如何返回結果給用戶等問題。

640.png

TDSQL-A采用MPP架構,其特性是share-nothing,數(shù)據(jù)分散在多個DN上,按照不同的分布鍵分布,并且不同的表可以自定義不同的分布鍵。如果CN收到了一條查詢,它會將這個任務分散到多個DN上并行執(zhí)行,從而提高執(zhí)行效率,最后CN獲得DN并行執(zhí)行的最后結果,匯總之后再返回給客戶端。

640 (1).png

1.2 原分布式執(zhí)行框架

這里先說明一下我們的原分布式執(zhí)行框架一個最主要的問題。下圖是一個簡單的Join查詢,如果Join查詢正好是在這個表的分布鍵上進行Join,則不涉及數(shù)據(jù)的重分布,可以直接在每個DN節(jié)點上進行Join,DN的結果匯總起來就是最終的查詢結果,這是最理想的情況。

但客戶的查詢往往比較復雜多樣,Join經(jīng)常會涉及不同節(jié)點之間的數(shù)據(jù)交換,Join的兩個表的Join鍵不一定是一個表的分布鍵,這種情況下就會涉及到數(shù)據(jù)的重分布。在TDSQL-A中,數(shù)據(jù)重分布是由Remote Subplan算子來執(zhí)行。在執(zhí)行的時候,Remote Subplan算子會并行地創(chuàng)建對應下層的執(zhí)行進程和對應的DN連接,每個DN都會創(chuàng)建對應其他DN的各個鏈接,這就會導致鏈接數(shù)和進程數(shù)急劇膨脹,給服務器造成很大的壓力。

640 (2).png

1.3 TDSQL-A分布式執(zhí)行框架

針對原分布式架構的缺點,我們設計了一套全新的分布式執(zhí)行框架。在這種執(zhí)行框架下,查詢執(zhí)行前CN會對查詢計劃進行分片,并創(chuàng)建DN上的各個執(zhí)行進程,每個DN的進程間不需要再建立冗余的進程及連接。這可以減少不必要的進程和連接,減輕服務器的負擔,并且能夠做到比較好的線性擴展性。數(shù)據(jù)交互則是通過中間的router——FN節(jié)點來進行數(shù)據(jù)交換,這是當前TDSQL-A的分布式執(zhí)行框架。

640.webp.jpg

二、查詢計劃分片策略

2.1 查詢計劃分片過程

之所以要對查詢計劃進行分片,主要是因為一個分布式的查詢計劃,在絕大多數(shù)情況下,必然會包含數(shù)據(jù)的重分布。在我們的執(zhí)行框架中,根據(jù)數(shù)據(jù)重分布進行查詢計劃的劃分。

首先包括數(shù)據(jù)重分布的代價在內(nèi),優(yōu)化器會生成一個代價估算最優(yōu)的執(zhí)行計劃。在這個執(zhí)行計劃上,我們會做計劃樹的劃分分片——把每一個數(shù)據(jù)重分布的節(jié)點下面的子數(shù)作為一個計劃的分片,再通過FID來對每一個計劃分片進行管理。

以下圖為例,假設有一個兩層的Hash Join,每一層涉及到一些對應的數(shù)據(jù)重分布,就會有一個四分片的查詢的產(chǎn)生。

640 (3).png

2.2 Agg算子執(zhí)行計劃

在分布式數(shù)據(jù)庫里面,對其他的算子,也會生成一個分布式的執(zhí)行計劃,比如OLAP場景里面經(jīng)常使用的執(zhí)行聚合計算的Agg算子。在聚合計算中,比如group id正好是表的分布鍵的情況下,可以生成單獨的分片,就像下圖中FID 1這樣的分片。每個Agg操作都是在DN本地執(zhí)行,最后匯總到CN上得到一個最終結果。但是在有些情況下,比如聚合鍵不是分布鍵的情況下,就會在最下層的節(jié)點上做部分的聚合操作,在上層的節(jié)點經(jīng)過數(shù)據(jù)重分布之后再做最后的聚合操作,得到最終結果。這就是一個分布式的Agg算子的執(zhí)行計劃。

640 (4).png

2.3 Sort算子&Limit算子執(zhí)行計劃

Sort算子還有Limit算子也是同樣的邏輯。

對于Sort算子,我們會在DN本地先做一次排序,經(jīng)過數(shù)據(jù)重分布后,在上層節(jié)點再進行歸并,最后得到最終的排序結果。

對于Limit算子,我們會把它進行下推。比如說下面這個例子中,這條搜索語句是查詢前100名的test order,這樣的話我們會把Limit算子進行下推,每個DN只返回Limit 100條數(shù)據(jù)給上層節(jié)點,上層節(jié)點在收到結果之后再進行合并排序,最后取Limit 100的結果作為最終結果返回給上層。

640 (5).png

三、異步執(zhí)行流程控制

3.1 異步執(zhí)行具體流程

在生成查詢計劃的分片后,CN會下發(fā)每個分片對應的執(zhí)行計劃片段,分別發(fā)送給各個DN,然后每個分片在每個執(zhí)行節(jié)點上會創(chuàng)建一個進程,執(zhí)行對應的執(zhí)行計劃。不同層級的進程異步啟動執(zhí)行,通過FN進行數(shù)據(jù)交互。

下圖中可以看到,這里有兩個查詢,分別是簡單的Join查詢,以及數(shù)據(jù)重分布的Join查詢。如果是傳統(tǒng)的數(shù)據(jù)庫執(zhí)行流程,就會先啟動下層節(jié)點,再啟動上層節(jié)點。但在我們設計的這種執(zhí)行框架下,F(xiàn)ID 1和FID 2是同步啟動的,它們之間通過FN來進行數(shù)據(jù)交互。

640 (6).png

如果在有兩個數(shù)據(jù)節(jié)點的情況下,Join查詢怎么啟動執(zhí)行進程呢?因為有兩個分片,還有兩個數(shù)據(jù)節(jié)點,所以在執(zhí)行的過程中,有四個進程在同時執(zhí)行。

最下面的這兩個分片,都屬于FID 2,但分別在DN 1和DN 2上執(zhí)行,執(zhí)行對應的計劃分片。對其中一個表進行掃描,再通過FN節(jié)點進行數(shù)據(jù)交換。上面的這兩個分片都屬于FID 1,分別在DN 1和DN 2上執(zhí)行,它們分別獲取自己所需要的數(shù)據(jù),同時執(zhí)行自己的執(zhí)行計劃分片。最終,兩個FID 1的執(zhí)行進程會把最終結果發(fā)送給CN。這四個進程是同步執(zhí)行的,在數(shù)據(jù)交換的時候通過FN來進行。

640.webp.jpg

3.2 自適應流程控制

TDSQL-A執(zhí)行框架最大的難點就在于進程間如何進行協(xié)調(diào)和控制。針對這個問題,我們設計了一個具有自適應特點的異步執(zhí)行的流程控制機制。它主要有以下三個方面的特點:

·靈活控制執(zhí)行進度。根據(jù)實際執(zhí)行情況,DN動態(tài)地控制各個進程之間的執(zhí)行進度。

·根據(jù)前端設置按需執(zhí)行,優(yōu)化資源利用,快速響應異常。比如前端發(fā)送Cancel請求時,能夠及時響應處理。如果任何執(zhí)行進程發(fā)生異常,也能夠快速響應處理。

·保證分布式事務一致性。涉及修改操作的分片會開啟事務,并且同步執(zhí)行這個事務的提交或者回滾等操作。

640.webp (1).jpg

下面我將分別從這三個方面來介紹一下這個異步執(zhí)行流程控制機制。

在各個進程同步執(zhí)行的情況下,如果有的進程出現(xiàn)執(zhí)行阻塞的情況,該怎樣互相協(xié)調(diào)呢?

以下圖為例,假設上層節(jié)點中的FID 1的這兩個執(zhí)行進程執(zhí)行比較慢,而下層FID 2的這兩個進程執(zhí)行進度比較快的時候,下層FID 2的兩個進程會源源不斷地向上層發(fā)送它們的執(zhí)行結果。如果不加控制的話,不僅會浪費下層FID 2的執(zhí)行資源,而且會造成網(wǎng)絡的阻塞。

針對這種情況,我們設計了進程間可以互相協(xié)調(diào)執(zhí)行進度的控制機制,主要通過數(shù)據(jù)流控制來實現(xiàn)。如果上層節(jié)點的執(zhí)行進度慢于預期的時候,下層節(jié)點會進行等待,等到上層節(jié)點能夠繼續(xù)執(zhí)行時,下層節(jié)點才會繼續(xù)做自己計劃分片的執(zhí)行,把數(shù)據(jù)發(fā)送給上層節(jié)點。這樣可以在執(zhí)行節(jié)點上達到資源分配和使用較優(yōu)的效果,空出來的網(wǎng)絡資源和CPU/IO資源就可以讓渡給其他查詢來執(zhí)行。

640.webp (2).jpg

我們的控制機制中除了數(shù)據(jù)流之外,還有控制流。由CN來監(jiān)聽并統(tǒng)一處理控制流消息。DN節(jié)點的執(zhí)行進程,又叫Dprocess,在執(zhí)行的過程中會隨時響應控制消息。以下圖為例,如果用戶執(zhí)行一個比較長的進程或者誤執(zhí)行了一個Query,在執(zhí)行幾分鐘后,不想再執(zhí)行了,就會給CN發(fā)送一個Cancel信號取消查詢,這時CN會把這個信號通過鏈接發(fā)送給每個執(zhí)行進程,DN上的執(zhí)行進程收到信號后就會終止執(zhí)行,及時把資源讓渡出來給其他的查詢使用。這是Cancel消息的處理過程。

640.webp (3).jpg

除了Cancel消息外,我們還處理Error信息。在執(zhí)行進程同步執(zhí)行的過程中,每個執(zhí)行進程之間通過FN來進行數(shù)據(jù)交換。如果其中一個進程發(fā)生Error,比如在處理的過程中資源不足,或者在處理過程中遇到數(shù)據(jù)錯誤或其他錯誤等,這時它會報Error信號,通過鏈接將這個信號上報給CN。CN在收到執(zhí)行進程Error消息后,會進行消息處理,然后下發(fā)給其他的執(zhí)行進程,讓它們終止執(zhí)行。也就是說,如果任何一個并行執(zhí)行的進程發(fā)生了錯誤,我們也能夠及時取消、結束這個查詢。

640.png

3.3 執(zhí)行流程示例

下圖是一個總體執(zhí)行流程的示例。左側是一個帶有數(shù)據(jù)重分布的Join查詢,它的整體執(zhí)行流程可以用右邊的這個圖來表示。四個執(zhí)行進程之間會有數(shù)據(jù)交換,是通過FN來交換數(shù)據(jù)流,最終結果也是通過FN數(shù)據(jù)流返還給CN,CN上還有一個后臺線程,通過控制流控制各個執(zhí)行進程之間的執(zhí)行,這就是整體的執(zhí)行構架。

640 (1).png

除了查詢語句外,我們還會遇到DML語句。DML語句即Insert、Update、Delete語句,它們需要進行分布式執(zhí)行事務的提交或者回滾操作。在執(zhí)行過程中,我們主要是把修改操作集中在一個分片內(nèi),然后在執(zhí)行修改操作的這個分片內(nèi)進行事務的開啟、提交和回滾等操作。這個事務的命令同樣也是通過控制線程來進行發(fā)送,其他線程也同樣是通過Cancel或者上報Error來處理控制消息。

這里舉一個最典型的例子。執(zhí)行Insert into語句時,如果后面跟的是Select From,也就是在其他的表中經(jīng)過查詢操作獲得一個結果集,把這個結果集插入到一個表中,此時我們在其他分片上執(zhí)行只讀操作,只在第一個包含Insert的分片上執(zhí)行修改操作,這個修改操作就涉及事務的提交和回滾。

640.webp.jpg

3.4 中止處理流程

這里重點介紹中止處理流程,它和Cancel流程不一樣。中止處理流程是CN在獲取了部分的查詢結果集后中止執(zhí)行。典型的應用場景是把查詢結果做分頁展示。在很多前端的應用中,查詢結果就是用分頁展示的形式展現(xiàn)在客戶端頁面上的。

比如一個查詢,第一頁可能有1000條查詢結果,下一頁則是下1000條查詢結果。CN在查詢執(zhí)行的時候,只要執(zhí)行獲取到1000條結果,就可以返回給前端,讓前端做展示或者處理。因為前端程序處理查詢結果也需要時間,在這時,后端就可以繼續(xù)執(zhí)行獲取下1000條查詢結果,這樣就能實現(xiàn)前端和后端并行執(zhí)行,取得執(zhí)行效率整體最優(yōu)化。

在這種執(zhí)行流程下,CN會先獲取前1000條結果——該數(shù)值用戶可以自由設置,在獲取到指定結果集之后CN先返回給前端,前端處理完之后,如果需要再獲取,CN就會繼續(xù)返回下一批結果。

如果前端查詢?nèi)∠?,比如用戶可能?頁或者6頁之后不想再看,或者是前端應用處理到第幾批數(shù)據(jù)之后不再處理直接返回,在這樣的情況下,查詢其實不需要再繼續(xù)執(zhí)行,這時CN會下發(fā)一個End query信號,然后在并行執(zhí)行流程上也會及時響應這個信號來結束查詢。下圖就是簡單的展示。

640.webp (1).jpg

左下角是分頁場景下的執(zhí)行性能對比。最左邊的這個柱子顯示的是,如果這個查詢在正常執(zhí)行情況下,在返回第一條結果的時候所需要的時間,第二個柱子是如果設置了fetch size是1000條時,它所需要執(zhí)行的時間。如果沒有設置fetch size,在傳統(tǒng)的執(zhí)行方式下,這個查詢的執(zhí)行時間是非常長的,但如果我們先設置返回1000條結果,這個查詢時間可以大幅縮小。同時在繼續(xù)執(zhí)行的時候,后續(xù)的每一批的查詢結果的執(zhí)行時間幾乎可以忽略不計。因為前端在接受查詢時,我們后端也在同時處理繼續(xù)獲取查詢結果。

四、子查詢執(zhí)行優(yōu)化

在OLAP場景下一些比較典型的包含有子查詢的執(zhí)行優(yōu)化。OLAP子查詢基本上可以分為兩類:一類是非相關的子查詢,一類是相關的子查詢。

4.1 非相關子查詢執(zhí)行

非相關的子查詢,指的是子查詢的結果集是一個固定的值,跟外層的查詢沒有關聯(lián)。對非相關子查詢,我們設計了“異步執(zhí)行、一次執(zhí)行”的機制。子查詢對我們的執(zhí)行框架來說,是另外的一個分片,它跟父查詢可以并行執(zhí)行。當父查詢需要子查詢的結果時,子查詢已經(jīng)執(zhí)行完畢了,父查詢可以直接獲取結果繼續(xù)執(zhí)行。

下圖中,F(xiàn)ID 3分片就是代表子查詢的執(zhí)行分片。Hash Join在執(zhí)行過程中,每個分片都是并行執(zhí)行的,在FID 2做掃描的時候,如果它不需要子查詢的結果,就可以不用等待FID 3的執(zhí)行結果。當它需要子查詢的執(zhí)行結果時,因為FID 3和FID 2是并行執(zhí)行,就可以直接獲取到這個結果并使用。這是非相關子查詢的執(zhí)行。

640.webp (2).jpg

4.2 相關子查詢執(zhí)行

更為復雜的是相關子查詢的執(zhí)行。在執(zhí)行過程中,相關子查詢的執(zhí)行結果是跟父查詢的傳遞條件是有關系的。

以下圖為例,在order 1和order 2的pid是相等的情況下,查詢會從order 2這個表中取出最大的tax值。這個tax的值再和外層的order 1的tax值做等值比較,最后獲取等值比較成立的那個結果,作為最終的查詢結果。

相關子查詢的執(zhí)行,一般情況是由父分片傳遞參數(shù)到子分片上,子分片會設置這個參數(shù)值,然后返回查詢結果。比如FID 2先做一個scan的操作,它在要獲取子查詢的值時,會先把order 1的pid先通過fragment之間的連接傳遞給FID 3,F(xiàn)ID 3在取得并設置了order 1的pid值后,執(zhí)行它本身的執(zhí)行計劃,最后獲取的結果再傳遞給FID 2,然后FID 2獲取結果后再繼續(xù)進行計算,可以看到這是一個非并行的執(zhí)行。之所以這樣,主要是因為子查詢FID 3的每一條執(zhí)行結果其實是和FID 2下發(fā)的參數(shù)值是有關的。因此它們倆不能并行執(zhí)行,這樣的子查詢執(zhí)行效率就比較低。

640.webp (3).jpg

針對這種情況,我們做了相關子查詢的優(yōu)化,會在計劃生成階段由優(yōu)化器自動改寫查詢計劃。在很多應用中,查詢語句可能是由前端應用自動生成的,并且數(shù)量很大,如果都用人工來進行優(yōu)化改寫,工作量會非常大。在這種情況下,我們在優(yōu)化器中實現(xiàn)了一套基于代數(shù)變換規(guī)則的自動改寫,會把相關子查詢,根據(jù)一定的規(guī)則改寫成等價的Join查詢,之后再進行其他優(yōu)化,生成最后的查詢計劃。

經(jīng)過優(yōu)化后,相關子查詢的性能提升非常明顯。下面這個圖就是在子查詢改寫之后,它的優(yōu)化性能對比??梢钥吹?,如果按照原來的執(zhí)行方式,每個子查詢每一次設置參數(shù)之后都需要執(zhí)行一次,整個查詢的執(zhí)行時間非常長。如果改寫成等價的Join查詢之后,它的執(zhí)行效率非常高。

640.png

立即登錄,閱讀全文
版權說明:
本文內(nèi)容來自于騰訊云數(shù)據(jù)庫,本站不擁有所有權,不承擔相關法律責任。文章內(nèi)容系作者個人觀點,不代表快出海對觀點贊同或支持。如有侵權,請聯(lián)系管理員(zzx@kchuhai.com)刪除!
掃碼登錄
打開掃一掃, 關注公眾號后即可登錄/注冊
加載中
二維碼已失效 請重試
刷新
賬號登錄/注冊
個人VIP
小程序
快出海小程序
公眾號
快出海公眾號
商務合作
商務合作
投稿采訪
投稿采訪
出海管家
出海管家