在Clickhouse中,我有一个大表A,其中包含以下列:
date, user_id, operator, active
在表A中,事件已经按照日期,user_id和运算符进行了预先汇总,而“活动”列表示在给定日期存在某种类型的用户活动。
表A分布在2个分片/服务器上:首先,我在每台服务器上创建了表A_local(PK是日期,user_id)。然后,我创建了分布式表A,以使用hash(userid,operator)作为分片键来合并本地表A_local。 User_id是高基数字段(几千万到几亿),而列“ operator”具有低基数(大约1000个不同的值)。每个user_id都属于一个操作符,也就是说,元组(user_id,operator)的基数与user_id本身相同。
我需要计算每个运营商在给定时间段内活跃超过N天的用户数。为此,我首先需要为每个user_id查找用户在给定时间段内处于活动状态的天数,这在subselect中进行。然后,在主选项中,我计算按操作员分组的用户。
SELECT
operator,
count() AS cnt_user
FROM
(
SELECT
user_id,
operator,
count() AS cnt
FROM A
WHERE date >= '2019-06-01' AND date <= '2019-08-31'
AND active = 1
GROUP BY
user_id,
operator
HAVING cnt >= 30
)
GROUP BY operator
使用user_id和operator进行分片的想法是将用户路由到不同的分片。这样,我希望完整的查询(选择和子选择)可以在每个分片/服务器上独立运行,然后最终汇总将通过较小的基数集执行:运算符->计数。
但是,当我在较长时间段(数月)内运行此查询时,Clickhouse抛出异常,告知已超出最大查询内存分配。如果我在本地表上运行相同的查询,则不会出现此类异常并返回结果。 Clickhouse首先将子选择中的所有记录合并到两个分片上,然后计算外部聚合。问题是如何重写查询或更改模式,以强制Clickhouse在本地执行两个聚合,然后在最后一步中合并低基数聚合(通过运算符)?我希望拥有user_id和operator的分片键可以自然地使Clickhouse做到这一点,但事实并非如此。
答案 0 :(得分:4)
每个分片
create view xxx as
SELECT
user_id,
operator,
count() AS cnt
FROM A_local
GROUP BY
user_id,
operator
HAVING cnt >= 30
create xxx_d Distributed(,xxx);
select ....
from xxx_d
WHERE date >= '2019-06-01' AND date <= '2019-08-31'
AND active = 1
GROUP BY operator
settings distributed_group_by_no_merge=1