偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

火山引擎在行為分析場(chǎng)景下的 ClickHouse JOIN 優(yōu)化

原創(chuàng) 精選
大數(shù)據(jù)
火山引擎增長(zhǎng)分析 DataFinder 基于 ClickHouse 來(lái)進(jìn)行行為日志的分析,ClickHouse 的主要版本是基于社區(qū)版改進(jìn)開(kāi)發(fā)的字節(jié)內(nèi)部版本。

1. 背景

火山引擎增長(zhǎng)分析 DataFinder 基于 ClickHouse 來(lái)進(jìn)行行為日志的分析,ClickHouse 的主要版本是基于社區(qū)版改進(jìn)開(kāi)發(fā)的字節(jié)內(nèi)部版本。主要的表結(jié)構(gòu):

圖片

事件表:存儲(chǔ)用戶行為數(shù)據(jù),以用戶 ID 分 shard 存儲(chǔ)。

--列出了主要的字段信息
CREATE TABLE tob_apps_all
(
`tea_app_id` UInt32, --應(yīng)用ID
`device_id` String DEFAULT '', --設(shè)備ID
`time` UInt64,--事件日志接受時(shí)間
`event` String,--事件名稱(chēng)
`user_unique_id` String,--用戶ID
`event_date` Date , --事件日志日期,由time轉(zhuǎn)換而來(lái)
`hash_uid` UInt64 --用戶ID hash過(guò)后的id,用來(lái)join降低內(nèi)存消耗
)│
```

用戶表:存儲(chǔ)用戶的屬性數(shù)據(jù),以用戶 ID 分 shard 存儲(chǔ)。

--列出了主要的字段信息
CREATE TABLE users_unique_all
(
`tea_app_id` UInt32, --應(yīng)用ID
`user_unique_id` String DEFAULT '', -- 用戶ID
`device_id` String DEFAULT '', -- 用戶最近的設(shè)備ID
`hash_uid` UInt64,--用戶ID hash過(guò)后的id,用來(lái)join降低內(nèi)存消耗
`update_time` UInt64,--最近一次更新時(shí)間
`last_active_date` Date --用戶最后活躍日期
)

設(shè)備表:存儲(chǔ)設(shè)備相關(guān)的數(shù)據(jù),以設(shè)備 ID 分 shard 存儲(chǔ)。

--列出了主要的字段信息
CREATE TABLE devices_all
(
`tea_app_id` UInt32, --應(yīng)用ID
`device_id` String DEFAULT '', --設(shè)備ID
`update_time` UInt64, --最近一次更新時(shí)間
`last_active_date` Date --用戶最后活躍日期
)

業(yè)務(wù)對(duì)象表:存儲(chǔ)業(yè)務(wù)對(duì)象相關(guān)的數(shù)據(jù),每個(gè) shard 存儲(chǔ)全量的數(shù)據(jù)。

--列出了主要的字段信息
CREATE TABLE rangers.items_all
(
`tea_app_id` UInt32,
`hash_item_id` Int64,
`item_name` String, --業(yè)務(wù)對(duì)象名稱(chēng)。比如商品
`item_id` String, --業(yè)務(wù)對(duì)象ID。比如商品id 1000001
`last_active_date` Date
)

1.1 業(yè)務(wù)挑戰(zhàn)

圖片

隨著接入應(yīng)用以及應(yīng)用的 DAU 日益增加,ClickHouse 表的事件量增長(zhǎng)迅速;并且基于行為數(shù)據(jù)需要分析的業(yè)務(wù)指標(biāo)越來(lái)越復(fù)雜,需要 JOIN 的表增多;我們遇到有一些涉及到 JOIN 的復(fù)雜 SQL 執(zhí)行效率低,內(nèi)存和 CPU 資源占用高,導(dǎo)致分析接口響應(yīng)時(shí)延和錯(cuò)誤率增加。

2. 關(guān)于 Clickhouse 的 JOIN

在介紹優(yōu)化之前,先介紹一下基本的 ClickHouse JOIN 的類(lèi)型和實(shí)現(xiàn)方式。

2.1 分布式 JOIN

SELECT 
et.os_name,
ut.device_id AS user_device_id
FROM tob_apps_all AS et
ANY LEFT JOIN
(
SELECT
device_id,
hash_uid
FROM users_unique_all
WHERE (tea_app_id = 268411) AND (last_active_date >= '2022-08-06')
) AS ut ON et.hash_uid = ut.hash_uid
WHERE (tea_app_id = 268411)
AND (event = 'app_launch')
AND (event_date = '2022-08-06')

基本執(zhí)行過(guò)程:

一個(gè) Clickhouse 節(jié)點(diǎn)作為 Coordinator 節(jié)點(diǎn),給每個(gè)節(jié)點(diǎn)分發(fā)子查詢(xún),子查詢(xún) sql(tob_apps_all 替換成本地表,users_unique_all 保持不變依然是分布式表)。

每個(gè)節(jié)點(diǎn)執(zhí)行 Coordinator 分發(fā)的 sql 時(shí),發(fā)現(xiàn) users_unique_all 是分布式表,就會(huì)去所有節(jié)點(diǎn)上去查詢(xún)以下 SQL(一共有 N*N。N 為 shard 數(shù)量)。

  1. SELECT device_id, hash_uid FROMusers_uniqueWHERE (tea_app_id = 268411) AND (last_active_date >= '2022-08-06')
  1. 每個(gè)節(jié)點(diǎn)從其他 N-1個(gè)節(jié)點(diǎn)拉取2中子查詢(xún)的全部數(shù)據(jù),全量存儲(chǔ)(內(nèi)存 or 文件),進(jìn)行本地 JOIN。
  2. Coordinator 節(jié)點(diǎn)從每個(gè)節(jié)點(diǎn)拉取3中的結(jié)果集,然后做處理返回給 client。

存在的問(wèn)題:

  1. 子查詢(xún)數(shù)量放大。
  2. 每個(gè)節(jié)點(diǎn)都全量存儲(chǔ)全量的數(shù)據(jù)。

2.2 分布式 Global JOIN

SELECT 
et.os_name,
ut.device_id AS user_device_id
FROM tob_apps_all AS et
GLOBAL ANY LEFT JOIN
(
SELECT
device_id,
hash_uid
FROM users_unique_all
WHERE (tea_app_id = 268411) AND (last_active_date >= '2022-08-06')
) AS ut ON et.hash_uid = ut.hash_uid
WHERE (tea_app_id = 268411)
AND (event = 'app_launch')
AND (event_date = '2022-08-06')

基本執(zhí)行過(guò)程:

  1. 一個(gè) Clickhouse 節(jié)點(diǎn)作為 Coordinator 節(jié)點(diǎn),分發(fā)查詢(xún)。在每個(gè)節(jié)點(diǎn)上執(zhí)行sql(tob_apps_all 替換成本地表,右表子查詢(xún)替換成別名 ut)。
  2. Coordinator 節(jié)點(diǎn)去其他節(jié)點(diǎn)拉取 users_unique_all 的全部數(shù)據(jù),然后分發(fā)到全部節(jié)點(diǎn)(作為1中別名表 ut 的數(shù)據(jù))。
  3. 每個(gè)節(jié)點(diǎn)都會(huì)存儲(chǔ)全量的2中分發(fā)的數(shù)據(jù)(內(nèi)存or文件),進(jìn)行本地 local join。
  4. Coordinator 節(jié)點(diǎn)從每個(gè)節(jié)點(diǎn)拉取3中的結(jié)果集,然后做處理返回給 client。

存在的問(wèn)題:

  1. 每個(gè)節(jié)點(diǎn)都全量存儲(chǔ)數(shù)據(jù)。
  2. 如果右表較大,分發(fā)的數(shù)據(jù)較大,會(huì)占用網(wǎng)絡(luò)帶寬資源。

2.3 本地 JOIN

SQL 里面只有本地表的 JOIN,只會(huì)在當(dāng)前節(jié)點(diǎn)執(zhí)行。

SELECT et.os_name,ut.device_id AS user_device_id
FROM tob_apps et any LEFT JOIN
(SELECT device_id,
hash_uid
FROM rangers.users_unique
WHERE tea_app_id = 268411
AND last_active_date>='2022-08-06') ut
ON et.hash_uid=ut.hash_uid
WHERE tea_app_id = 268411
AND event='app_launch'
AND event_date='2022-08-06'

2.3.1 Hash join

  • 右表全部數(shù)據(jù)加載到內(nèi)存,再在內(nèi)存構(gòu)建 hash table。key 為 joinkey。
  • 從左表分批讀取數(shù)據(jù),從右表 hash table匹配數(shù)據(jù)。
  • 優(yōu)點(diǎn)是:速度快 缺點(diǎn)是:右表數(shù)據(jù)量大的情況下占用內(nèi)存。

2.3.2 Merge join?

  • 對(duì)右表排序,內(nèi)部 block 切分,超出內(nèi)存部分 flush 到磁盤(pán)上,內(nèi)存大小通過(guò)參數(shù)設(shè)定。
  • 左表基于 block 排序,按照每個(gè) block 依次與右表 merge。
  • 優(yōu)點(diǎn)是:能有效控制內(nèi)存 缺點(diǎn)是:大數(shù)據(jù)情況下速度會(huì)慢。

優(yōu)先使用 hash join 當(dāng)內(nèi)存達(dá)到一定閾值后再使用 merge join,優(yōu)先滿足性能要求。

3. 解決方案

圖片

3.1 避免JOIN

3.1.1 數(shù)據(jù)預(yù)生成

數(shù)據(jù)預(yù)生成(由 Spark/Flink 或者 Clickhouse 物化視圖產(chǎn)出數(shù)據(jù)),形成大寬表,基于單表的查詢(xún)是 ClickHouse 最為擅長(zhǎng)的場(chǎng)景。

我們有個(gè)指標(biāo),實(shí)現(xiàn)的 SQL 比較復(fù)雜(如下),每次實(shí)時(shí)查詢(xún)很耗時(shí),我們單獨(dú)建了一個(gè)表 table,由 Spark 每日構(gòu)建出這個(gè)指標(biāo),查詢(xún)時(shí)直接基于 table 查詢(xún)。

SELECT event_date,count(distinct uc1) AS uv,sum(value) AS sum_value, ......
FROM
(SELECT event_date,hash_uid AS uc1,sum(et.float_params{ 'amount' }) AS value, count(1) AS cnt, value*cnt AS multiple
FROM tob_apps_all et GLOBAL ANY LEFT JOIN
(SELECT hash_uid AS join_key,int_profiles{ '$ab_time_34' }*1000 AS first_time
FROM users_unique_all
WHERE app_id = 10000000 AND last_active_date >= '2022-07-19' AND first_time is NOT null) upt
ON et.hash_uid=upt.join_key
WHERE (查詢(xún)條件)
GROUP BY uc1,event_date)
GROUP BY event_date;

數(shù)據(jù)量2300W,查詢(xún)時(shí)間由7秒->0.008秒。當(dāng)然這種方式,需要維護(hù)額外的數(shù)據(jù)構(gòu)建任務(wù)??偟乃悸肪褪遣灰?ClickHouse 實(shí)時(shí)去 JOIN。

圖片

3.1.2 使用 IN 代替 JOIN

JOIN 需要基于內(nèi)存構(gòu)建 hash table 且需要存儲(chǔ)右表全部的數(shù)據(jù),然后再去匹配左表的數(shù)據(jù)。而 IN 查詢(xún)會(huì)對(duì)右表的全部數(shù)據(jù)構(gòu)建 hash set,但是不需要匹配左表的數(shù)據(jù),且不需要回寫(xiě)數(shù)據(jù)到 block。

比如:

SELECT event_date, count()
FROM tob_apps_all et global any INNER JOIN
(SELECT hash_uid AS join_key
FROM users_unique_all
WHERE app_id = 10000000
AND last_active_date >= '2022-01-01') upt
ON et.hash_uid = upt.join_key
WHERE app_id = 10000000
AND event_date >= '2022-01-01'
AND event_date <= '2022-08-02'
GROUP BY event_date

可以改成如下形式:

SELECT event_date,
count()
FROM tob_apps_all
WHERE app_id = 10000000
AND event_date >= '2022-01-01'
AND event_date <= '2022-08-02'
AND hash_uid global IN
(SELECT hash_uid
FROM users_unique_all
WHERE (tea_app_id = 10000000)
AND (last_active_date >= '2022-01-01') )
GROUP BY event_date

如果需要從右表提取出屬性到外層進(jìn)行計(jì)算,則不能使用 IN 來(lái)代替 JOIN。

相同的條件下,上面的測(cè)試 SQL,由 JOIN 時(shí)的16秒優(yōu)化到了 IN 查詢(xún)時(shí)的11秒。

圖片

?

3.2 更快的 JOIN

3.2.1 優(yōu)先本地 JOIN

數(shù)據(jù)預(yù)先相同規(guī)則分區(qū)

也就是 Colocate JOIN。優(yōu)先將需要關(guān)聯(lián)的表按照相同的規(guī)則進(jìn)行分布,查詢(xún)時(shí)就不需要分布式的 JOIN。

SELECT 
et.os_name,
ut.device_id AS user_device_id
FROM tob_apps_all AS et
ANY LEFT JOIN
(
SELECT
device_id,
hash_uid
FROM users_unique_all
WHERE (tea_app_id = 268411) AND (last_active_date >= '2022-08-06')
) AS ut ON et.hash_uid = ut.hash_uid
WHERE (tea_app_id = 268411)
AND (event = 'app_launch')
AND (event_date = '2022-08-06')
settings distributed_perfect_shard=1

比如事件表 tob_apps_all 和用戶表 users_unique_all 都是按照用戶 ID 來(lái)分 shard 存儲(chǔ)的,相同的用戶的兩個(gè)表的數(shù)據(jù)都在同一個(gè) shard 上,因此這兩個(gè)表的 JOIN 就不需要分布式 JOIN 了。

distributed_perfect_shard 這個(gè) settings key 是字節(jié)內(nèi)部 ClickHouse 支持的,設(shè)置過(guò)這個(gè)參數(shù),指定執(zhí)行計(jì)劃時(shí)就不會(huì)再執(zhí)行分布式 JOIN 了。

基本執(zhí)行過(guò)程:

  1. 一個(gè) ClickHouse 節(jié)點(diǎn)作為 Coordinator 節(jié)點(diǎn),分發(fā)查詢(xún)。在每個(gè)節(jié)點(diǎn)上執(zhí)行 sql(tob_apps_all、users_unique_all替換成本地表)。
  2. 每個(gè)節(jié)點(diǎn)都執(zhí)行1中分發(fā)的本地表 join 的 SQL(這一步不再分發(fā)右表全量的數(shù)據(jù))。
  3. 數(shù)據(jù)再回傳到 coordinator 節(jié)點(diǎn),然后返回給 client。
數(shù)據(jù)冗余存儲(chǔ)

如果一個(gè)表的數(shù)據(jù)量比較小,可以不分 shard 存儲(chǔ),每個(gè) shard 都存儲(chǔ)全量的數(shù)據(jù),例如我們的業(yè)務(wù)對(duì)象表。查詢(xún)時(shí),不需要分布式 JOIN,直接在本地進(jìn)行 JOIN 即可。

SELECT count()
FROM tob_apps_all AS et
ANY LEFT JOIN
(
SELECT item_id
FROM items_all
WHERE (tea_app_id = 268411)
) AS it ON et.item_id = it.item_id
WHERE (tea_app_id = 268411)
AND (event = 'app_launch')
AND (event_date = '2022-08-06')
settings distributed_perfect_shard=1

例如這個(gè) SQL,items_all 表每個(gè) shard 都存儲(chǔ)同樣的數(shù)據(jù),這樣也可以避免分布式 JOIN 帶來(lái)的查詢(xún)放大和全表數(shù)據(jù)分發(fā)問(wèn)題。

3.2.2 更少的數(shù)據(jù)

不論是分布式 JOIN 還是本地 JOIN,都需要盡量讓少的數(shù)據(jù)參與 JOIN,既能提升查詢(xún)速度也能減少資源消耗。

SQL 下推

ClickHouse 對(duì) SQL 的下推做的不太好,有些復(fù)雜的 SQL 下推會(huì)失效。因此,我們手動(dòng)對(duì) SQL 做了下推,目前正在測(cè)試基于查詢(xún)優(yōu)化器來(lái)幫助實(shí)現(xiàn)下推優(yōu)化,以便讓 SQL 更加簡(jiǎn)潔。

下推的 SQL:

SELECT 
et.os_name,
ut.device_id AS user_device_id
FROM tob_apps_all AS et
ANY LEFT JOIN
(
SELECT
device_id,
hash_uid
FROM users_unique_all
WHERE (tea_app_id = 268411)
AND (last_active_date >= '2022-08-06'
AND 用戶屬性條件 1 OR 用戶屬性條件 2)
) AS ut ON et.hash_uid = ut.hash_uid
WHERE (tea_app_id = 268411)
AND (event = 'app_launch')
AND (event_date = '2022-08-06')
settings distributed_perfect_shard=1

對(duì)應(yīng)的不下推的 SQL:

SELECT 
et.os_name,
ut.device_id AS user_device_id
FROM tob_apps_all AS et
ANY LEFT JOIN
(
SELECT
device_id,
hash_uid
FROM rangers.users_unique_all
WHERE (tea_app_id = 268411)
AND (last_active_date >= '2022-08-06')
) AS ut ON et.hash_uid = ut.hash_uid
WHERE (tea_app_id = 268411)
AND (event = 'app_launch')
AND (event_date = '2022-08-06')
AND (ut.用戶屬性條件 1 OR ut.用戶屬性條件 2)
settings distributed_perfect_shard=1

可以看到,不下推的 SQL 更加簡(jiǎn)潔,直接基于 JOIN 過(guò)后的寬表進(jìn)行過(guò)濾。但是 ClickHouse 可能會(huì)將不滿足條件的 users_unique_all 數(shù)據(jù)也進(jìn)行 JOIN。

我們使用中有一個(gè)復(fù)雜的 case,用戶表過(guò)濾條件不下推有1千萬(wàn)+,SQL 執(zhí)行了3000秒依然執(zhí)行超時(shí),而做了下推之后60秒內(nèi)就執(zhí)行成功了。

圖片

3.2.3 Clickhouse 引擎層優(yōu)化

一個(gè) SQL 實(shí)際在 Clickhouse 如何執(zhí)行,對(duì) SQL 的執(zhí)行時(shí)間和資源消耗至關(guān)重要。社區(qū)版的 Clickhouse 在執(zhí)行模型和 SQL 優(yōu)化器上還要改進(jìn)的空間,尤其是復(fù)雜 SQL 以及多 JOIN 的場(chǎng)景下。

執(zhí)行模型優(yōu)化

社區(qū)版的 Clickhouse 目前還是一個(gè)兩階段執(zhí)行的執(zhí)行模型。第一階段,Coordinator 在收到查詢(xún)后,將請(qǐng)求發(fā)送給對(duì)應(yīng)的 Worker 節(jié)點(diǎn)。第二階段,Worker 節(jié)點(diǎn)完成計(jì)算,Coordinator 在收到各 Worker 節(jié)點(diǎn)的數(shù)據(jù)后進(jìn)行匯聚和處理,并將處理后的結(jié)果返回。

圖片

有以下幾個(gè)問(wèn)題:

  1. 第二階段的計(jì)算比較復(fù)雜時(shí),Coordinator 的節(jié)點(diǎn)計(jì)算壓力大,容易成為瓶頸。
  2. 不支持 shuffle join,hash join 時(shí)右表為大表時(shí)構(gòu)建慢,容易 OOM。
  3. 對(duì)復(fù)雜查詢(xún)的支持不友好。

字節(jié)跳動(dòng) ClickHouse 團(tuán)隊(duì)為了解決上述問(wèn)題,改進(jìn)了執(zhí)行模型,參考其他的分布式數(shù)據(jù)庫(kù)引擎(例如 Presto 等),將一個(gè)復(fù)雜的 Query 按數(shù)據(jù)交換情況切分成多個(gè) Stage,各 Stage 之間則通過(guò) Exchange 完成數(shù)據(jù)交換。根據(jù) Stage 依賴(lài)關(guān)系定義拓?fù)浣Y(jié)構(gòu),產(chǎn)生 DAG 圖,并根據(jù) DAG 圖調(diào)度 Stage。例如兩表 JOIN,會(huì)先調(diào)度左右表讀取 Stage,之后再調(diào)度 JOIN 這個(gè) Stage, JOIN 的 Stage 依賴(lài)于左右表的 Stage。

圖片

舉個(gè)例子

SELECT 
et.os_name,
ut.device_id AS user_device_id,
dt.hash_did AS device_hashid
FROM tob_apps_all AS et
GLOBAL ANY LEFT JOIN
(
SELECT
device_id,
hash_uid
FROM users_unique_all
WHERE (tea_app_id = 268411) AND (last_active_date >= '2022-08-06')
) AS ut ON et.hash_uid = ut.hash_uid
GLOBAL ANY LEFT JOIN
(
SELECT
device_id,
hash_did
FROM devices_all
WHERE (tea_app_id = 268411) AND (last_active_date >= '2022-08-06')
) AS dt ON et.device_id = dt.device_id
WHERE (tea_app_id = 268411) AND (event = 'app_launch') AND (event_date = '2022-08-06')
LIMIT 10

Stage執(zhí)行模型基本過(guò)程(可能的):

  1. 讀取 tob_apps_all 數(shù)據(jù),按照 join key(hash_uid)進(jìn)行 shuffle,數(shù)據(jù)分發(fā)到每個(gè)節(jié)點(diǎn)。這是一個(gè)Stage。
  2. 讀取 users_unique_all 數(shù)據(jù),按照 join key(hash_uid)進(jìn)行 shuffle,數(shù)據(jù)分發(fā)到每個(gè)節(jié)點(diǎn)。這是一個(gè) Stage。
  3. 上述兩個(gè)表的數(shù)據(jù),在每個(gè)節(jié)點(diǎn)上的數(shù)據(jù)進(jìn)行本地JOIN,然后再按照 join key(device_id) 進(jìn)行 shuffle。這是一個(gè) Stage。
  4. 讀取 devices_all 數(shù)據(jù),按照 join key(device_id)進(jìn)行 shuffle,這是一個(gè)Stage。
  5. 第3步、第4步的數(shù)據(jù),相同 join key(device_id) 的數(shù)據(jù)都在同一個(gè)節(jié)點(diǎn)上,然后進(jìn)行本地JOIN,這是一個(gè) Stage。
  6. 匯總數(shù)據(jù),返回 limit 10 的數(shù)據(jù)。這是一個(gè) Stage。

統(tǒng)計(jì)效果如下:

圖片

查詢(xún)優(yōu)化器

有了上面的 stage 的執(zhí)行模型,可以靈活調(diào)整 SQL 的執(zhí)行順序,字節(jié)跳動(dòng) Clickhouse 團(tuán)隊(duì)自研了查詢(xún)優(yōu)化器,根據(jù)優(yōu)化規(guī)則(基于規(guī)則和代價(jià)預(yù)估)對(duì) SQL 的執(zhí)行計(jì)劃進(jìn)行轉(zhuǎn)換,一個(gè)執(zhí)行計(jì)劃經(jīng)過(guò)優(yōu)化規(guī)則后會(huì)變成另外一個(gè)執(zhí)行計(jì)劃,能夠準(zhǔn)確的選擇出一條效率最高的執(zhí)行路徑,然后構(gòu)建 Stage 的 DAG 圖,大幅度降低查詢(xún)時(shí)間。

下圖描述了整個(gè)查詢(xún)的執(zhí)行流程,從 SQL parse 到執(zhí)行期間所有內(nèi)容全部進(jìn)行了重新實(shí)現(xiàn)(其中紫色模塊),構(gòu)建了一套完整的且規(guī)范的查詢(xún)優(yōu)化器。

圖片

還是上面的三表 JOIN 的例子,可能的一個(gè)執(zhí)行過(guò)程是:

  1. 查詢(xún)優(yōu)化器發(fā)現(xiàn) users_unique_all 表與 tob_apps_all 表的分 shard 規(guī)則一樣(基于用戶 ID ),所以就不會(huì)先對(duì)表按 join key 進(jìn)行 shuffle,users_unique 與 tob_apps 直接基于本地表 JOIN,然后再按照 join key(device_id)進(jìn)行 shuffle。這是一個(gè) Stage。
  2. 查詢(xún)優(yōu)化器根據(jù)規(guī)則或者代價(jià)預(yù)估決定設(shè)備表 devices_all 是需要 broadcast join 還是 shuffle join。
  1. 如果 broadcast join:在一個(gè)節(jié)點(diǎn)查到全部的 device 數(shù)據(jù),然后分發(fā)到其他節(jié)點(diǎn)。這是一個(gè) Stage。
  2. 如果 shuffle join:在每個(gè)節(jié)點(diǎn)對(duì) device 數(shù)據(jù)按照 join key(device_id) 進(jìn)行 shuffle。這是一個(gè) Stage。
  1. 匯總數(shù)據(jù),返回 limit 10 的數(shù)據(jù)。這是一個(gè) Stage。

效果:

可以看到,查詢(xún)優(yōu)化器能優(yōu)化典型的復(fù)雜的 SQL 的執(zhí)行效率,縮短執(zhí)行時(shí)間。

圖片

4. 總結(jié)

ClickHouse 最為擅長(zhǎng)的領(lǐng)域是一個(gè)大寬表來(lái)進(jìn)行查詢(xún),多表 JOIN 時(shí)Clickhouse 性能表現(xiàn)不佳。作為業(yè)內(nèi)領(lǐng)先的用戶分析與運(yùn)營(yíng)平臺(tái),火山引擎增長(zhǎng)分析 DataFinder 基于海量數(shù)據(jù)做到了復(fù)雜指標(biāo)能夠秒級(jí)查詢(xún)。本文介紹了我們是如何優(yōu)化 Clickhouse JOIN 查詢(xún)的。

主要有以下幾個(gè)方面:

  1. 減少參與 JOIN 的表以及數(shù)據(jù)量。
  2. 優(yōu)先使用本地 JOIN,避免分布式 JOIN 帶來(lái)的性能損耗。
  3. 優(yōu)化本地 JOIN,優(yōu)先使用內(nèi)存進(jìn)行 JOIN。
  4. 優(yōu)化分布式 JOIN 的執(zhí)行邏輯,依托于字節(jié)跳動(dòng)對(duì) ClickHouse 的深度定制化。
責(zé)任編輯:未麗燕 來(lái)源: 字節(jié)跳動(dòng)技術(shù)團(tuán)隊(duì)
相關(guān)推薦

2022-12-07 08:31:45

ClickHouse并行計(jì)算數(shù)據(jù)

2021-04-14 14:31:37

私有云veStack系統(tǒng)

2021-09-16 10:47:09

數(shù)字化

2022-05-13 07:26:28

策略模式設(shè)計(jì)模式

2022-06-30 09:50:15

火山引擎ByteHouse

2021-08-31 16:17:50

數(shù)字化

2023-10-19 14:55:22

火山引擎擁塞控制算法

2025-05-20 08:35:00

2023-09-15 14:24:54

ByteHouseClickHouse開(kāi)源

2021-09-16 14:30:02

數(shù)字化

2021-11-11 21:35:48

數(shù)字化

2023-08-30 15:33:02

火山引擎
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)