<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
不同的 Join 方式非常依賴於對 Doris 中資料劃分方式的透徹理解。因此先在這裡列舉出必要的基礎知識。
首先,在 Doris 中資料都以表(Table)的形式進行邏輯上的描述。
在 Doris 的儲存引擎中,使用者資料被水平劃分為若干個資料分片(Tablet,也稱作資料分桶 Bucket)。每個 Tablet 包含若干資料行。各個 Tablet 之間的資料沒有交集,並且在物理上是獨立儲存的。
一個 Tablet 只屬於一個資料分割區(Partition)。而一個 Partition 包含若干個 Tablet。因為 Tablet 在物理上是獨立儲存的,所以可以視為 Partition 在物理上也是獨立的。Tablet 是資料移動、複製等操作的最小物理儲存單元。
若干個 Partition 組成一個 Table。Partition 可以視為是邏輯上最小的管理單元。資料的匯入與刪除,僅能針對一個 Partition 進行。
Doris 支援兩層的資料劃分。第一層是 Partition,支援 Range 和 List 的劃分方式。第二層是 Bucket(Tablet),僅支援 Hash 的劃分方式。也可以僅使用一層分割區。使用一層分割區時,只支援 Bucket 劃分。
下圖說明 Table、Partition、Bucket(Tablet) 的關係:
特別注意:
Doris 中的 Partition 和 Bucket 定義可能和某些其它資料庫系統的定義有一些差異,下面配以一個具體的建表語句為例來說明:
CREATE TABLE IF NOT EXISTS example_db.expamle_range_tbl ( `user_id` LARGEINT NOT NULL COMMENT "使用者id", `date` DATE NOT NULL COMMENT "資料灌入日期時間", `timestamp` DATETIME NOT NULL COMMENT "資料灌入的時間戳", `city` VARCHAR(20) COMMENT "使用者所在城市", `age` SMALLINT COMMENT "使用者年齡", `sex` TINYINT COMMENT "使用者性別", `last_visit_date` DATETIME REPLACE DEFAULT "1970-01-01 00:00:00" COMMENT "使用者最後一次存取時間", `cost` BIGINT SUM DEFAULT "0" COMMENT "使用者總消費", `max_dwell_time` INT MAX DEFAULT "0" COMMENT "使用者最大停留時間", `min_dwell_time` INT MIN DEFAULT "99999" COMMENT "使用者最小停留時間" ) ENGINE=OLAP AGGREGATE KEY(`user_id`, `date`, `timestamp`, `city`, `age`, `sex`) PARTITION BY RANGE(`date`) ( PARTITION `p201701` VALUES LESS THAN ("2017-02-01"), PARTITION `p201702` VALUES LESS THAN ("2017-03-01"), PARTITION `p201703` VALUES LESS THAN ("2017-04-01") ) DISTRIBUTED BY HASH(`user_id`) BUCKETS 16 PROPERTIES ( "replication_num" = "3" );
綠色高亮:Partition,此例中使用一個 date 欄位進行分割區
藍色高亮:Bucket,此例中使用 user_id 欄位為作為分佈列
建立分割區時不可新增範圍重疊的分割區
有兩種分割區方式:
分割區方式 | 一般用法 |
---|---|
Range | 通常按時間分割區,以方便地管理新舊資料 |
List | 支援的型別更豐富,分割區值為列舉值。只有當資料為目標分割區列舉值其中之一時,才可以命中分割區 |
作為分散式的 MPP 資料庫, 在 Join 的過程中是需要進行資料的 Shuffle。資料需要進行拆分排程,才能保證最終的 Join 結果是正確的。舉個簡單的例子,假設關係 S 和 R 進行Join,N 表示參與 Join 計算的節點的數量;T 則表示關係的 Tuple 數目。
目前 Doris 支援的 Join 方式有以上 4 種,這 4 種方式靈活度和適用性是從高到低的,對資料分佈的要求越來越嚴,但 Join 計算的效能則通過降低網路開銷而越來越好。
Join 方式的選擇是 FE 生成分散式計劃階段會考慮的事項之一。在 FE 進行分散式計劃時,優先選擇的順序為(總是會優先選擇預期效能最好的):Colocate Join -> Bucket Shuffle Join -> Broadcast Join -> Shuffle Join。
Colocate 以及 Bucket Shuffle 是可遇不可求的。當無法使用它們時,Doris會自動嘗試進行 Broadcast Join,如果預估小表過大則會自動切換至 Shuffle Join。
但是使用者可以通過顯式 Hint 來強制使用期望的 Join 型別,比如:
select * from test join [shuffle] baseall on test.k1 = baseall.k1;
原理比較簡單,這裡不展開。
當 Join 條件命中了左表的資料分佈列時,Broadcast 以及 Shuffle Join 會有非必要的網路傳輸開銷。而 Bucket Shuffle Join 旨在解決這類問題,通過對左表實現本地性計算優化,來減少左表資料在節點間的傳輸耗時,從而加速查詢。
以上的例子中,Join 的等值表示式命中了表 A(左表)的資料分佈列。Bucket Shuffle Join 會根據表 A 的資料分佈資訊,將表 B(右表)的資料傳送到對應表 A 的資料計算節點。
定性分析上:
可以理解為在資料分佈滿足一定條件的前提下,減少一切不必要的網路傳輸開銷,實現完全的計算在地化來加速查詢。同時因為沒有網路傳輸開銷,BE 節點可以擁有更高的並行度,從而進一步提升 Join 效能。
要理解這個演演算法,需要先了解兩個術語:
和 Buckets Sequence 這一概念:
一個表的資料,最終會根據分桶列值 Hash、對桶數取模後落在某一個分桶內。假設一個 Table 的分桶數為 8,則共有 [0, 1, 2, 3, 4, 5, 6, 7] 8 個分桶(Bucket),我們稱這樣一個序列為一個 BucketsSequence。每個 Bucket 內會有一個或多個資料分片(Tablet)。當表為單分割區表時,一個 Bucket 內僅有一個 Tablet。如果是多分割區表,則會有多個(因為多個 Partition 中的不同 Tablet 會被劃分到相同的 Bucket)。
Colocation Join 功能,是將一組擁有相同 CGS 的 Table 組成一個 CG。並保證這些 Table 對應的資料分片會落在同一個 BE 節點上。使得當 CG 內的表進行分桶列上的 Join 操作時,可以通過直接進行本地資料 Join,減少資料在節點間的傳輸耗時。
因此關鍵問題就轉變為了「如何保證這些 Table 對應的資料分片會落在同一個 BE 節點上?」
通過同一 CG 內的 Table 必須保證以下屬性相同實現:
分桶列,即在建表語句中 DISTRIBUTED BY HASH(col1, col2, ...) 中指定的列。分桶列決定了一張表的資料通過哪些列的值進行 Hash 劃分到不同的 Tablet 中。同一 CG 內的 Table 必須保證分桶列的型別和數量完全一致,並且桶數一致,才能保證多張表的資料分片能夠一一對應的進行分佈控制。
同一個 CG 內所有表的所有分割區(Partition)的副本數必須一致。如果不一致,可能出現某一個 Tablet 的某一個副本,在同一個 BE 上沒有其他的表分片的副本對應。不過,同一個 CG 內的表,分割區的個數、範圍以及分割區列的型別不要求一致。
在固定了分桶列和分桶數後,同一個 CG 內的表會擁有相同的 BucketsSequence。而副本數決定了每個分桶內的 Tablet 的多個副本,存放在哪些 BE 上。假設 BucketsSequence 為 [0, 1, 2, 3, 4, 5, 6, 7],BE 節點有 [A, B, C, D] 4個。則一個可能的資料分佈如下:
+---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ | 0 | | 1 | | 2 | | 3 | | 4 | | 5 | | 6 | | 7 | +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ | A | | B | | C | | D | | A | | B | | C | | D | | | | | | | | | | | | | | | | | | B | | C | | D | | A | | B | | C | | D | | A | | | | | | | | | | | | | | | | | | C | | D | | A | | B | | C | | D | | A | | B | +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+
CG 內所有表的資料都會按照上面的規則進行統一分佈,這樣就保證了,分桶列值相同的資料都在同一個 BE 節點上,可以進行本地資料 Join。其核心思想是「兩次對映」,保證相同的 Distributed Key 的資料會被對映到相同的 Bucket Sequence,再保證 Bucket Sequence 對應的 Bucket 對映到相同的 BE 節點:
通過查詢計劃可以檢查一個查詢是否使用了 Colocate Join,同時計劃中的 Exchange Node 也被去掉了,會將 ScanNode 直接設定為 Hash Join Node 的孩子節點。
DESC SELECT * FROM tbl1 INNER JOIN tbl2 ON (tbl1.k2 = tbl2.k2); -- 在 Hash Join 節點會顯示: -- colocate: true/false
Colocate Join 十分適合幾張表按照相同欄位分桶,並高頻根據固定的欄位 Join 的場景。這樣可以將資料預先儲存到相同的分桶中,實現本地計算。
Doris 在進行 Hash Join 計算時會在右表構建一個 Hash Table,左表流式地通過右表的 Hash Table 從而得出 Join 結果。而 Runtime Filter 就是充分利用了右表的 Hash Table 構建階段去做一些額外的事情。
在右表生成 Hash Table 的時,同時生成一個基於 Hash Table 資料的一個過濾條件,然後下推到左表的資料掃描節點。通過這樣的方式,Doris 可以在執行時進行資料過濾。
假如左表是一張大表,右表是一張小表,那麼利用下推到左表的過濾條件就可以把絕大多數 Join 層要過濾的資料在資料讀取時就提前過濾(如果能夠下推到引擎層,還能夠利用 Doris 針對 Key 列過濾的延遲物化),從而大幅度地提升 Join 查詢的效能。
Runtime Filter 在查詢規劃時生成,在 HashJoinNode 中構建,在 ScanNode 中應用。比如 T1(行數 10w) 和 T2(行數 2k) 的 Join 操作:
| > HashJoinNode < | | | | | 100000 | 2000 | | | | OlapScanNode OlapScanNode | ^ ^ | | 100000 | 2000 | T1 T2 |
顯而易見對 T2 掃描資料要遠遠快於 T1,如果我們主動等待一段時間再掃描 T1,等 T2 將掃描的資料記錄交給 HashJoinNode 後,HashJoinNode 根據 T2 的資料計算出一個過濾條件,比如 T2 資料的最大和最小值,或者構建一個 Bloom Filter,接著將這個過濾條件發給等待掃描 T1 的 ScanNode,後者應用這個過濾條件,將過濾後的資料交給 HashJoinNode,從而減少 probe hash table 的次數和網路開銷,這個過濾條件就是 Runtime Filter,效果如下:
| > HashJoinNode < | | | | | 6000 | 2000 | | | | OlapScanNode OlapScanNode | ^ ^ | | 100000 | 2000 | T1 T2 |
如果能將過濾條件(Runtime Filter)下推到儲存引擎,則某些情況可以利用索引(比如 Join 列為 Key 列,可以利用延遲物化能力)來直接減少掃描的資料量,從而大大減少掃描耗時,效果如下:
| > HashJoinNode < | | | | | 6000 | 2000 | | | | OlapScanNode OlapScanNode | ^ ^ | | 6000 | 2000 | T1 T2 |
可見,和謂詞下推、分割區裁剪不同,Runtime Filter 是在執行時動態生成的過濾條件,即在查詢執行時解析 Join 條件確定過濾表示式,並將表示式下推給正在讀取左表的 ScanNode,從而減少掃描的資料量,進而減少 probe hash table 的次數,避免不必要的 IO 和網路傳輸。因為其執行時生效的特性,官方認為它是 Adaptive Query Execution 的一種應用。
根據上面的例子,可以推匯出場景滿足以下的條件時,使用 Runtime Filter 的效果會比較好:
Doris 支援 3 種 Runtime Filter:
工作原理和優劣總結如下:
Runtime Filter 型別 | 工作原理 | 適用場景 | 優點 | 缺點 |
---|---|---|---|---|
IN | 子查詢的方式,實現上是將一個 Hashset 下推到 Scan 節點 | Broadcast Join | 開銷小,過濾效果明顯且快速 | 右表超過一定資料量時會失效,目前 Doris 設定的閾值是 1024 |
Min/Max | 通過右表構建一個 Range 範圍,然後將它下推到 Scan 節點 | 通用 | 開銷小 | 僅對數值型別有效果;對數值以外型別無法使用 |
BloomFilter | 通過右表構建一個 BloomFilter,然後將它下推到 Scan 節點 | 通用 | 通用性較好,適用於各種型別、效果也較好 | 設定比較複雜且計算成本較高;當過濾率較低或者左表資料較少時,可能導致效能降低 |
一些使用的注意事項(比較細節了,後面考慮結合程式碼再深入理解):
開啟 Runtime Filter 後,左表的 ScanNode 會為每一個分配給自己的 Runtime Filter 等待一段時間再掃描資料,即如果 ScanNode 被分配了 3 個 Runtime Filter,那麼它最多會等待 3000ms。
因為 Runtime Filter 的構建和合並均需要時間,ScanNode 會嘗試將等待時間內到達的 Runtime Filter 下推到儲存引擎,如果超過等待時間後,ScanNode 會使用已經到達的 Runtime Filter 直接開始掃描資料。
如果 Runtime Filter 在 ScanNode 開始掃描之後到達,則 ScanNode 不會將該 Runtime Filter 下推到儲存引擎,而是對已經從儲存引擎掃描上來的資料,在 ScanNode 上基於該 Runtime Filter 使用表示式過濾,之前已經掃描的資料則不會應用該 Runtime Filter,這樣得到的中間資料規模會大於最優解,但可以避免嚴重的劣化。
如果叢集比較繁忙,並且叢集上有許多資源密集型或長耗時的查詢,可以考慮增加等待時間,以避免複雜查詢錯過優化機會。如果叢集負載較輕,並且叢集上有許多隻需要幾秒的小查詢,可以考慮減少等待時間,以避免每個查詢增加 1s 的延遲。
有了前面兩表 Join 的 Runtime Filter 鋪墊,再來看 Join Reorder 的優化,邏輯關係上就能夠理順了。
Doris 目前的 Join Reorder 演演算法是基於 RBO 的,邏輯描述如下:
可以發現前兩條,都是在朝著讓「右表」更小的方向去優化,而最後一條則是從演演算法的效能上來考慮。
REF
https://www.jb51.net/article/266004.htm
https://www.jb51.net/article/266000.htm
以上就是Apache Doris Join 優化原理詳解的詳細內容,更多關於Apache Doris Join 優化的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45