0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

ClickHouse內(nèi)幕(3)基于索引的查詢優(yōu)化

京東云 ? 來源:jf_75140285 ? 作者:jf_75140285 ? 2024-06-11 10:46 ? 次閱讀

ClickHouse索引采用唯一聚簇索引的方式,即Part內(nèi)數(shù)據(jù)按照order by keys有序,在整個查詢計劃中,如果算子能夠有效利用輸入數(shù)據(jù)的有序性,對算子的執(zhí)行性能將有巨大的提升。本文討論ClickHouse基于索引的查詢算子優(yōu)化方式。

在整個查詢計劃中Sort、Distinct、聚合這3個算子相比其他算子比如:過濾、projection等有如下幾個特點:1.算子需要再內(nèi)存中保存狀態(tài),內(nèi)存代價高;2.算子計算代價高;3.算子會阻斷執(zhí)行pipeline,待所有數(shù)據(jù)計算完整后才會向下游輸出數(shù)據(jù)。所以上算子往往是整個查詢的瓶頸算子。

本文詳細(xì)討論,3個算子基于索引的查詢優(yōu)化前后,在計算、內(nèi)存和pipeline阻斷上的影響。

實驗前準(zhǔn)備:

后續(xù)的討論主要基于實驗進(jìn)行。

CREATE TABLE test_in_order
(
    `a` UInt64,
    `b` UInt64,
    `c` UInt64,
    `d` UInt64
)
ENGINE = MergeTree
ORDER BY (a, b);

表中總共有3個part,每個part數(shù)據(jù)量4條。

PS: 用戶可以在插入數(shù)據(jù)前提前關(guān)閉后臺merge,以避免part合并成一個,如果part合并成一個將影響查詢并行度,可能對實驗有影響,以下查詢可以關(guān)閉后臺merge:system stop merges test_in_order

一、Sort算子

如果order by查詢的order by字段與表的order by keys的前綴列匹配,那么可以根據(jù)數(shù)據(jù)的有序特性對Sort算子進(jìn)行優(yōu)化。

1.Sort算子實現(xiàn)方式

首先看下不能利用主鍵有序性的場景,即對于order by查詢的order by字段與表的order by keys的前綴列不匹配。比如下面的查詢:

query_1: EXPLAIN PIPELINE SELECT b FROM read_in_order ORDER BY b ASC

它的執(zhí)行計劃如下:

┌─explain───────────────────────────────┐
│ (Expression)                          │
│ ExpressionTransform                   │
│   (Sorting)                           │
│   MergingSortedTransform 3 → 1        │
│     MergeSortingTransform × 3         │
│       LimitsCheckingTransform × 3     │
│         PartialSortingTransform × 3   │
│           (Expression)                │
│           ExpressionTransform × 3     │
│             (ReadFromMergeTree)       │
│             MergeTreeThread × 3 0 → 1 │
└───────────────────────────────────────┘

排序算法由3個Transform組成,其中

1)PartialSortingTransform對單個Chunk進(jìn)行排序;

2)MergeSortingTransform對單個stream進(jìn)行排序;

3)MergingSortedTransform合并多個有序的stream進(jìn)行全局sort-merge排序

wKgaomZnupqAPI15AAB2MeV7qvk592.png


如果查詢的order by字段與表的order by keys的前綴列匹配,那么可以根據(jù)數(shù)據(jù)的有序特性對查詢進(jìn)行優(yōu)化,優(yōu)化開關(guān):optimize_read_in_order。

2.匹配索引列的查詢

以下查詢的order by字段與表的order by keys的前綴列匹配

query_3: EXPLAIN PIPELINE SELECT b FROM test_in_order ORDER BY a ASC, b ASCSETTINGS optimize_read_in_order = 0 -- 關(guān)閉read_in_order優(yōu)化

查看order by語句的pipeline執(zhí)行計劃

┌─explain───────────────────────────┐
│ (Expression)                      │
│ ExpressionTransform               │
│   (Sorting)                       │
│   MergingSortedTransform 3 → 1    │
│     MergeSortingTransform × 3     │
│       (Expression)                │
│       ExpressionTransform × 3     │
│         (ReadFromMergeTree)       │
│         MergeTreeThread × 3 0 → 1 │
└───────────────────────────────────┘

此時order by算子的算法

1)首先MergeSortingTransform對輸入的stream進(jìn)行排序

2)然后MergingSortedTransform將多個排好序的stream進(jìn)行合并,并輸出一個整體有序的stream,也是最終的排序結(jié)果。

這里有個疑問在關(guān)閉read_in_order優(yōu)化的查詢計劃中,系統(tǒng)直接默認(rèn)了MergeSortingTransform的輸入在Chunk內(nèi)是有序的,這里其實是一個默認(rèn)優(yōu)化,因為order by查詢的order by字段與表的order by keys的前綴列匹配,所以數(shù)據(jù)在Chunk內(nèi)部一定是有序的。

3. 開啟優(yōu)化optimize_read_in_order

┌─explain──────────────────────────┐
│ (Expression)                     │
│ ExpressionTransform              │
│   (Sorting)                      │
│   MergingSortedTransform 3 → 1   │
│     (Expression)                 │
│     ExpressionTransform × 3      │
│       (ReadFromMergeTree)        │
│       MergeTreeInOrder × 3 0 → 1 │
└──────────────────────────────────┘

4. 優(yōu)化分析

打開optimize_read_in_order后:

1.對于計算方面:算法中只有一個MergingSortedTransform,省略了單個stream內(nèi)排序的步驟

2.由于內(nèi)存方面:由于MergeSortingTransform是消耗內(nèi)存最大的步驟,所以優(yōu)化后可以節(jié)約大量的內(nèi)存

3.對于poipeline阻塞:MergeSortingTransform會阻塞整個pipeline,所以優(yōu)化后也消除了對pipeline的阻塞

二、Distinct算子

如果distinct查詢的distinct字段與表的order by keys的前綴列匹配,那么可以根據(jù)數(shù)據(jù)的有序特性對Distinct算子進(jìn)行優(yōu)化,優(yōu)化開關(guān):optimize_distinct_in_order。通過以下實驗進(jìn)行說明:

1. Distinct算子實現(xiàn)方式

查看distinct語句的pipeline執(zhí)行計劃

query_2: EXPLAIN PIPELINE SELECT DISTINCT * FROM woo.test_in_order SETTINGS optimize_distinct_in_order = 0 -- 關(guān)閉distinct in order優(yōu)化
┌─explain─────────────────────────────┐
│ (Expression)                        │
│ ExpressionTransform                 │
│   (Distinct)                        │
│   DistinctTransform                 │
│     Resize 3 → 1                    │
│       (Distinct)                    │
│       DistinctTransform × 3         │
│         (Expression)                │
│         ExpressionTransform × 3     │
│           (ReadFromMergeTree)       │
│           MergeTreeThread × 3 0 → 1 │
└─────────────────────────────────────┘

Distinct算子采用兩階段的方式,首先第一個DistinctTransform在內(nèi)部進(jìn)行初步distinct,其并行度為3,可以簡單的認(rèn)為有3個線程在同時執(zhí)行。然后第二個DistinctTransform進(jìn)行final distinct。

每個DistinctTransform的計算方式為:首先構(gòu)建一個HashSet數(shù)據(jù)結(jié)構(gòu),然后根據(jù)HashSet,構(gòu)建一個Filter Mask(如果當(dāng)前key存在于HashSet中,則過濾掉),最后過濾掉不需要的數(shù)據(jù)。

2.開啟優(yōu)化optimize_distinct_in_order

┌─explain────────────────────────────────┐
│ (Expression)                           │
│ ExpressionTransform                    │
│   (Distinct)                           │
│   DistinctTransform                    │
│     Resize 3 → 1                       │
│       (Distinct)                       │
│       DistinctSortedChunkTransform × 3 │
│         (Expression)                   │
│         ExpressionTransform × 3        │
│           (ReadFromMergeTree)          │
│           MergeTreeThread × 3 0 → 1    │
└────────────────────────────────────────┘

可以看到初步distinct和final distinct采用了不同的transform,DistinctSortedChunkTransform和DistinctTransform。

DistinctSortedChunkTransform:對單個stream內(nèi)的數(shù)據(jù)進(jìn)行distinct操作,因為distinct列跟表的order by keys的前綴列匹配,scan算子讀取數(shù)據(jù)的時候一個stream只從一個part內(nèi)讀取數(shù)據(jù),那么每個distinct transform輸入的數(shù)據(jù)就是有序的。所以distinct算法有:

DistinctSortedChunkTransform算法一:

Transform中保留最后一個輸入的數(shù)據(jù)作為狀態(tài),對于每個輸入的新數(shù)據(jù)如果跟保留的狀態(tài)相同,那么忽略,如果不同則將上一個狀態(tài)輸出給上一個算子,然后保留當(dāng)前的數(shù)據(jù)最為狀態(tài)。這種算法對于在整個stream內(nèi)部全局去重時間和空間復(fù)雜度都有極大的降低。

wKgaomZnup2AV9P5AAAkb6cOov0046.png


DistinctSortedStreamTransform算法二:(ClickHouse采用的)

Transform對與每個Chunk(ClickHouse中Transform數(shù)據(jù)處理的基本單位,默認(rèn)大約6.5w行),首先將相同的數(shù)據(jù)劃分成多個Range,并設(shè)置一個mask數(shù)組,然后將相同的數(shù)據(jù)刪除掉,最后返回刪除重復(fù)數(shù)據(jù)的Chunk。

wKgZomZnup2AVsteAAA1RbKTsnk642.png


3. 優(yōu)化分析

打開optimize_distinct_in_order后:主要對于第一階段的distinct步驟進(jìn)行了優(yōu)化,從基于HashSet過濾的算法到基于連續(xù)相同值的算法。

1.對于計算方面:優(yōu)化后的算法,省去了Hash計算,但多了判斷相等的步驟,在不同數(shù)據(jù)基數(shù)集大小下,各有優(yōu)劣。

2.由于內(nèi)存方面:優(yōu)化后的算法,不需要存儲HashSet

3.對于poipeline阻塞:優(yōu)化前后都不會阻塞pipeline

三、聚合算子

如果group by查詢的order by字段與表的order by keys的前綴列匹配,那么可以根據(jù)數(shù)據(jù)的有序特性對聚合算子進(jìn)行優(yōu)化,優(yōu)化開關(guān):optimize_aggregation_in_order。

1.聚合算子實現(xiàn)方式

查看group by語句的pipeline執(zhí)行計劃:

query_4: EXPLAIN PIPELINE SELECT a FROM test_in_order GROUP BY a SETTINGS optimize_aggregation_in_order = 0 -- 關(guān)閉read_in_order優(yōu)化
┌─explain─────────────────────────────┐
│ (Expression)                        │
│ ExpressionTransform × 8             │
│   (Aggregating)                     │
│   Resize 3 → 8                      │
│     AggregatingTransform × 3        │
│       StrictResize 3 → 3            │
│         (Expression)                │
│         ExpressionTransform × 3     │
│           (ReadFromMergeTree)       │
│           MergeTreeThread × 3 0 → 1 │
└─────────────────────────────────────┘

對于聚合算子的整體算法沒有在執(zhí)行計劃中完整顯示出來,其宏觀上采用兩階段的聚合算法,其完整算法如下:1.AggregatingTransform進(jìn)行初步聚合,這一步可以并行計算;2.ConvertingAggregatedToChunksTransform進(jìn)行第二階段聚合。(PS:為簡化起見,忽略two level HashMap,和spill to disk的介紹)。

2.開啟優(yōu)化optimize_aggregation_in_order

執(zhí)行計劃如下:

┌─explain───────────────────────────────────────┐
│ (Expression)                                  │
│ ExpressionTransform × 8                       │
│   (Aggregating)                               │
│   MergingAggregatedBucketTransform × 8        │
│     Resize 1 → 8                              │
│       FinishAggregatingInOrderTransform 3 → 1 │
│         AggregatingInOrderTransform × 3       │
│           (Expression)                        │
│           ExpressionTransform × 3             │
│             (ReadFromMergeTree)               │
│             MergeTreeInOrder × 3 0 → 1        │
└───────────────────────────────────────────────┘

可以看到打開optimize_aggregation_in_order后aggregating算法由三個步驟組成:

1)首先AggregatingInOrderTransform會將stream內(nèi)連續(xù)的相同的key進(jìn)行預(yù)聚合,預(yù)聚合后在當(dāng)前stream內(nèi)相同keys的數(shù)據(jù)只會有一條;

2)FinishAggregatingInOrderTransform將接收到的多個stream內(nèi)的數(shù)據(jù)進(jìn)行重新分組使得輸出的chunk間數(shù)據(jù)是有序的,假設(shè)前一個chunk中g(shù)roup by keys最大的一條數(shù)據(jù)是5,當(dāng)前即將輸出的chunk中沒有大于5的數(shù)據(jù);

3)MergingAggregatedBucketTransform的作用是進(jìn)行最終的merge aggregating。

wKgaomZnup2ARICmAABfrfxtQaI394.png


FinishAggregatingInOrderTransform的分組算法如下:

假設(shè)有3個stream當(dāng)前算子會維護(hù)3個Chunk,每一次選取在當(dāng)前的3個Chunk內(nèi)找到最后一條數(shù)據(jù)的最小值,比如初始狀態(tài)最小值是5,然后將3個Chunk內(nèi)所有小于5的數(shù)據(jù)一次性取走,如此反復(fù)如果一個Chunk被取光,需要從改stream內(nèi)拉取新的Chunk。

wKgZomZnup6AEeZ2AABVTVDACO0969.png


這種算法保證了每次FinishAggregatingInOrderTransform向下游輸出的Chunk的最大值小于下一次Chunk的最小值,便于后續(xù)步驟的優(yōu)化。

3.優(yōu)化分析

打開optimize_aggregation_in_order后:主要對于第一階段的聚合步驟進(jìn)行了優(yōu)化,從基于HashMap的算法到基于連續(xù)相同值的算法。

1.對于計算方面:優(yōu)化后的算法,減少了Hash計算,但多了判斷相等的步驟,在不同數(shù)據(jù)基數(shù)集大小下,各有優(yōu)劣。

2.由于內(nèi)存方面:優(yōu)化前后無差別

3.對于poipeline阻塞:優(yōu)化前后無差別

四、優(yōu)化小結(jié)

在整個查詢計劃中Sort、Distinct、聚合這3個算子算子往往是整個查詢的瓶頸算子,所以值得對其進(jìn)行深度優(yōu)化。ClickHouse通過利用算子輸入數(shù)據(jù)的有序性,優(yōu)化算子的算法或者選擇不同的算法,在計算、內(nèi)存和pipeline阻塞三個方面均有不同程度的優(yōu)化。

審核編輯 黃宇

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • Pipeline
    +關(guān)注

    關(guān)注

    0

    文章

    28

    瀏覽量

    9332
  • 算子
    +關(guān)注

    關(guān)注

    0

    文章

    16

    瀏覽量

    7249
收藏 人收藏

    評論

    相關(guān)推薦

    內(nèi)幕揭秘:電流檢測放大器的滿量程和動態(tài)范圍注意事項

    電子發(fā)燒友網(wǎng)站提供《內(nèi)幕揭秘:電流檢測放大器的滿量程和動態(tài)范圍注意事項.pdf》資料免費下載
    發(fā)表于 09-12 09:11 ?0次下載
    <b class='flag-5'>內(nèi)幕</b>揭秘:電流檢測放大器的滿量程和動態(tài)范圍注意事項

    MATLAB中的矩陣索引

    對矩陣進(jìn)行索引是從矩陣中選擇或修改部分元素的一種方式。MATLAB 有幾種索引樣式,它們不僅功能強(qiáng)大、靈活,而且可讀性強(qiáng)、表現(xiàn)力強(qiáng)。矩陣是 MATLAB 用來組織和分析數(shù)據(jù)的一個核心組件,索引是以可理解的方式有效操作矩陣的關(guān)鍵。
    的頭像 發(fā)表于 09-05 09:28 ?301次閱讀
    MATLAB中的矩陣<b class='flag-5'>索引</b>

    一文了解MySQL索引機(jī)制

    接觸MySQL數(shù)據(jù)庫的小伙伴一定避不開索引,索引的出現(xiàn)是為了提高數(shù)據(jù)查詢的效率,就像書的目錄一樣。 某一個SQL查詢比較慢,你第一時間想到的就是“給某個字段加個
    的頭像 發(fā)表于 07-25 14:05 ?197次閱讀
    一文了解MySQL<b class='flag-5'>索引</b>機(jī)制

    供應(yīng)鏈場景使用ClickHouse最佳實踐

    關(guān)于ClickHouse的基礎(chǔ)概念這里就不做太多的贅述了,ClickHouse官網(wǎng)都有很詳細(xì)說明。結(jié)合供應(yīng)鏈數(shù)字化團(tuán)隊在使用ClickHouse時總結(jié)出的一些注意事項,尤其在命名方面要求研發(fā)嚴(yán)格遵守
    的頭像 發(fā)表于 07-18 15:05 ?218次閱讀
    供應(yīng)鏈場景使用<b class='flag-5'>ClickHouse</b>最佳實踐

    優(yōu)化嵌入式DSP軟件的編譯器

    確定哪個索引或一組索引對于優(yōu)化很重要取決于應(yīng)用程序開發(fā)人員的目標(biāo)。例如,性能優(yōu)化意味著開發(fā)人員可以使用速度較慢或成本較低的 DSP 來完成相同數(shù)量的工作。
    發(fā)表于 05-03 09:45 ?169次閱讀
    <b class='flag-5'>優(yōu)化</b>嵌入式DSP軟件的編譯器

    MySQL聯(lián)表查詢優(yōu)化

    條數(shù)據(jù),本意想查出
    的頭像 發(fā)表于 04-24 12:33 ?483次閱讀
    MySQL聯(lián)表<b class='flag-5'>查詢</b><b class='flag-5'>優(yōu)化</b>

    Redis官方搜索引擎來了,性能炸裂!

    RediSearch 是一個 Redis 模塊,為 Redis 提供查詢、二級索引和全文搜索功能。
    的頭像 發(fā)表于 02-21 10:01 ?1991次閱讀
    Redis官方搜<b class='flag-5'>索引</b>擎來了,性能炸裂!

    谷歌搜索引優(yōu)化的各個方面和步驟

    谷歌搜索引擎是最受歡迎和廣泛使用的搜索引擎之一,為了使你的網(wǎng)站在谷歌上更好地排名并提高曝光度,你可以采取一些谷歌搜索引優(yōu)化的步驟。 使用關(guān)鍵字研究工具,如Google AdWords
    的頭像 發(fā)表于 01-25 10:29 ?779次閱讀

    導(dǎo)致MySQL索引失效的情況以及相應(yīng)的解決方法

    導(dǎo)致MySQL索引失效的情況以及相應(yīng)的解決方法? MySQL索引的目的是提高查詢效率,但有些情況下索引可能會失效,導(dǎo)致查詢變慢或效果不如預(yù)期
    的頭像 發(fā)表于 12-28 10:01 ?687次閱讀

    Mysql索引是什么東西?索引有哪些特性?索引是如何工作的?

    作為開發(fā)人員,碰到了執(zhí)行時間較長的 sql 時,基本上大家都會說” 加個索引吧”。但是索引是什么東西,索引有哪些特性,下面和大家簡單討論一下。
    的頭像 發(fā)表于 12-24 16:20 ?1069次閱讀
    Mysql<b class='flag-5'>索引</b>是什么東西?<b class='flag-5'>索引</b>有哪些特性?<b class='flag-5'>索引</b>是如何工作的?

    春興精工實控人孫潔曉涉內(nèi)幕交易獲緩刑三年

    公開信息顯示,早在2018年1月,孫潔曉因涉嫌內(nèi)幕交易被證監(jiān)會立案調(diào)查。2019年3月,公司披露了證監(jiān)會的行政處罰決定書,孫被禁止處理持有的股票以及罰款。同年10月,孫因涉嫌內(nèi)幕交易罪被取保候?qū)?。隨著這次司法判決的下發(fā),此事終于
    的頭像 發(fā)表于 12-18 09:46 ?551次閱讀

    Cascades查詢優(yōu)化器基本原理分析

    優(yōu)化器一般由三個組件組成:統(tǒng)計信息收集、開銷模型、計劃列舉。 如圖 2 所示,開銷模型使用收集到的統(tǒng)計信息以及構(gòu)造的不同開銷公式,估計某個特定查詢計劃的成本,幫助優(yōu)化器從眾多備選方案中找到開銷最低的計劃。
    的頭像 發(fā)表于 12-15 09:38 ?418次閱讀
    Cascades<b class='flag-5'>查詢</b><b class='flag-5'>優(yōu)化</b>器基本原理分析

    sql語句where條件查詢

    的細(xì)節(jié),包括使用的操作符、條件的組合、多張表的查詢、條件的性能優(yōu)化等方面。 首先,讓我們了解一下WHERE子句中常用的操作符。在SQL中,常用的操作符包括: 比較操作符: 等于(=):用于判斷兩個值是否相等。 不等于( ):用于判斷兩個值是否不相等
    的頭像 發(fā)表于 11-23 11:28 ?1015次閱讀

    Format函數(shù)可以傳遞負(fù)索引

    是的, format() 函數(shù)可以傳遞負(fù)索引。在使用 format() 函數(shù)進(jìn)行字符串格式化時,可以使用索引來指定要替換的參數(shù)位置。正索引從0開始,負(fù)索引從-1開始,表示相對于參數(shù)列表
    的頭像 發(fā)表于 11-22 09:45 ?345次閱讀

    ClickHouse 聯(lián)合創(chuàng)始人、前 Google 副總裁 Yury 到訪杭州玖章算術(shù)公司,雙方建立生態(tài)合作

    10 月 31 日,ClickHouse 聯(lián)合創(chuàng)始人 Yury 到訪未來科技城,與玖章算術(shù)創(chuàng)始人葉正盛和國際總經(jīng)理 Ni Demai 展開溝通與推進(jìn)合作。ClickHouse 是深受開發(fā)者青睞的實時
    的頭像 發(fā)表于 11-17 11:23 ?836次閱讀
    <b class='flag-5'>ClickHouse</b> 聯(lián)合創(chuàng)始人、前 Google 副總裁 Yury 到訪杭州玖章算術(shù)公司,雙方建立生態(tài)合作