在工作中遇到了Clickhouse distributed 分布式聚合查询,二次聚合问题:
全局表的分布键值取cityHash64进行分片,相同的target_value 被写入同一个分片中,这样对target_value 进行聚合操作,
SQL语句应该分别在每个分片进行查询(SQL根据全局表的分布键值下推),然后把各个分片的聚合结果传输到SQL下发节点,直接返回结果:
1)SQL下发后,分片将SQL中的全局表转换为本地表;
2)直接运行对自己分片的本地表进行聚合查询;
3)将每个分片运行结果传输到SQL下发节点,返回给客户端;
但是,在实际使用中,并未出现希望的场景。
查看了SQL的执行计划得知:
1)SQL下发后,分片将SQL中的全局表转换为本地表;
2)每个分片将where条件的结果分别查出;
3)每个分片,将查出的中间数据,传输到SQL下发节点;
4)由下发节点来进行中间数据的聚合操作,得出SQL最终结果集,返回给客户端;
1、表结构如下
----全局表
create table test_all
(
target_type String,
target_value String,
cnd_type String,
cnd_value String,
target_time DateTime
) ENGINE= Distributed('ch_name','test_db','test_local',cityHash64(target_value ));
----本地表
create table test_local
(
target_type String,
target_value String,
cnd_type String,
cnd_value String,
target_time DateTime
) ENGINE= MergeTree
Partition by toDate(target_time)
order by (target_value ,cnd_value );
2、SQL如下
select
target_value ,target_type,cnd_value ,cnd_type ,count() matchCount
from test_all where toDate(target_time) >=toDate('2021-05-09') and toDate(target_time) <=toDate('2021-06-09')
group by target_value ,target_type,cnd_value ,cnd_type
having matchCount>7;
时间范围内数据总量为1.1亿条,经过筛选与排序后的数据油7千万条,运行时间50-60s之间。
如果换成本地表,则需要SQL运行7s左右:
select
target_value ,target_type,cnd_value ,cnd_type ,count() matchCount
from test_all where toDate(target_time) >=toDate('2021-05-09') and toDate(target_time) <=toDate('2021-06-09')
group by target_value ,target_type,cnd_value ,cnd_type
having matchCount>7;
3、问题解决
1)在分布式(DistributedMergeTree)表中用target_value 做分片键;
2)在分片表(也叫本地表)中用target_value 做order by
3)然后设置参数distributed_group_by_no_merge = 1
这个参数(默认为0,表示默认功能关闭)含义就是本地表做完聚合后,不再在分布式表中聚合所有数据。因此需要确保相同的target_value 值出现在同一个分片上(步骤1确保了这一点)。这样在每个分片上做group by之后,在分布式表做数据汇总即可。从而提升了查询性能。
4、优化后SQL
select
target_value ,target_type,cnd_value ,cnd_type ,count() matchCount
from test_local where toDate(target_time) >=toDate('2021-05-09') and toDate(target_time) <=toDate('2021-06-09')
group by target_value ,target_type,cnd_value ,cnd_type
having matchCount>7
settings distributed_group_by_no_merge = 1;
运行时间由原来的50-60s,将为17s左右。