如何避免在分布式表上合并高基数子选择聚合

时间:2019-09-06 16:50:37

标签: clickhouse

在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做到这一点,但事实并非如此。

1 个答案:

答案 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