Skip to content
ClickHouse Docs
ClickHouse DocsClickHouse Docs

AggregatingMergeTree 表引擎

该引擎继承自 MergeTree,并修改了数据分区片段合并的逻辑。ClickHouse 会将所有具有相同主键 (更准确地说,具有相同排序键) 的行合并为一行 (在单个数据分区片段内) ,该行存储聚合函数状态的组合。

你可以将 AggregatingMergeTree 表用于增量数据聚合,也可用于聚合 materialized view。

你可以在下面的视频中查看如何使用 AggregatingMergeTree 和聚合函数的示例:

该引擎会处理以下类型的所有列:

如果 AggregatingMergeTree 能将行数减少几个数量级,那么它就是合适的选择。

创建表

CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
    name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
    name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
    ...
) ENGINE = AggregatingMergeTree()
[PARTITION BY expr]
[ORDER BY expr]
[SAMPLE BY expr]
[TTL expr]
[SETTINGS name=value, ...]

有关请求参数的说明,请参阅请求描述

查询子句

创建 AggregatingMergeTree 表时,所需的子句与创建 MergeTree 表时相同。

创建表的已弃用方法
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
    name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
    name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
    ...
) ENGINE [=] AggregatingMergeTree(date-column [, sampling_expression], (primary, key), index_granularity)

所有参数的含义都与 MergeTree 中相同。

SELECT 和 INSERT

要插入数据,请使用带有聚合 -State- 函数的 INSERT SELECT 查询。 从 AggregatingMergeTree 表中查询数据时,请使用 GROUP BY 子句,并使用与插入数据时相同的聚合函数,但要加上 -Merge 后缀。

SELECT 查询结果中,AggregateFunction 类型的值在所有 ClickHouse 输出格式中都采用特定于实现的二进制表示。例如,如果你使用 SELECT 查询将数据转储为 TabSeparated 格式,就可以再通过 INSERT 查询将该转储重新加载。

aggregated materialized view 示例

以下示例假设您已有一个名为 test 的数据库。如果该数据库尚不存在,请使用以下命令创建:

CREATE DATABASE test;

现在创建包含原始数据的表 test.visits

CREATE TABLE test.visits
 (
    StartDate DateTime64 NOT NULL,
    CounterID UInt64,
    Sign Nullable(Int32),
    UserID Nullable(Int32)
) ENGINE = MergeTree ORDER BY (StartDate, CounterID);

接下来,您需要创建一个 AggregatingMergeTree 表,用于存储 AggregationFunction,以跟踪总访问次数和独立用户数量。

创建一个 AggregatingMergeTree materialized view,监听 test.visits 表,并使用 AggregateFunction 类型:

CREATE TABLE test.agg_visits (
    StartDate DateTime64 NOT NULL,
    CounterID UInt64,
    Visits AggregateFunction(sum, Nullable(Int32)),
    Users AggregateFunction(uniq, Nullable(Int32))
)
ENGINE = AggregatingMergeTree() ORDER BY (StartDate, CounterID);

创建一个 materialized view,将数据从 test.visits 填充至 test.agg_visits

CREATE MATERIALIZED VIEW test.visits_mv TO test.agg_visits
AS SELECT
    StartDate,
    CounterID,
    sumState(Sign) AS Visits,
    uniqState(UserID) AS Users
FROM test.visits
GROUP BY StartDate, CounterID;

将数据插入 test.visits 表:

INSERT INTO test.visits (StartDate, CounterID, Sign, UserID)
VALUES (1667446031000, 1, 3, 4), (1667446031000, 1, 6, 3);

数据已写入 test.visitstest.agg_visits 两张表。

要获取聚合数据,请对 materialized view test.visits_mv 执行如下查询,例如 SELECT ... GROUP BY ...

SELECT
    StartDate,
    sumMerge(Visits) AS Visits,
    uniqMerge(Users) AS Users
FROM test.visits_mv
GROUP BY StartDate
ORDER BY StartDate;
┌───────────────StartDate─┬─Visits─┬─Users─┐
│ 2022-11-03 03:27:11.000 │      9 │     2 │
└─────────────────────────┴────────┴───────┘

再向 test.visits 中添加几条记录,但这次为其中一条记录使用不同的时间戳:

INSERT INTO test.visits (StartDate, CounterID, Sign, UserID)
VALUES (1669446031000, 2, 5, 10), (1667446031000, 3, 7, 5);

再次运行 SELECT 查询,将返回以下输出:

┌───────────────StartDate─┬─Visits─┬─Users─┐
│ 2022-11-03 03:27:11.000 │     16 │     3 │
│ 2022-11-26 07:00:31.000 │      5 │     1 │
└─────────────────────────┴────────┴───────┘

在某些情况下,您可能希望避免在写入时对行进行预聚合,将聚合开销从写入时推迟到合并时。通常,为避免报错,需要在 materialized view 定义的 GROUP BY 子句中包含所有不参与聚合的列。不过,您可以配合设置 optimize_on_insert = 0 (默认为开启状态) ,使用 initializeAggregation 函数来实现这一目的。此时无需再使用 GROUP BY

CREATE MATERIALIZED VIEW test.visits_mv TO test.agg_visits
AS SELECT
    StartDate,
    CounterID,
    initializeAggregation('sumState', Sign) AS Visits,
    initializeAggregation('uniqState', UserID) AS Users
FROM test.visits;

Tuple 元素聚合

启用 allow_tuple_element_aggregation 设置后,Tuple 列会被递归展平,使每个叶子元素都能独立参与聚合。这意味着,Tuple 中的 AggregateFunctionSimpleAggregateFunction 子列会按照各自的函数进行聚合,就像它们是顶层列一样。

排序键中的 Tuple 子列不参与聚合。非聚合子列会被视为普通列 (保留其第一个值) 。

CREATE TABLE agg_tuples
(
    key UInt32,
    metrics Tuple(
        total_visits SimpleAggregateFunction(sum, UInt64),
        unique_users SimpleAggregateFunction(max, UInt64)
    )
) ENGINE = AggregatingMergeTree()
ORDER BY key
SETTINGS allow_tuple_element_aggregation = 1;

INSERT INTO agg_tuples VALUES (1, (100, 5));
INSERT INTO agg_tuples VALUES (1, (200, 8));
INSERT INTO agg_tuples VALUES (2, (50, 3));

OPTIMIZE TABLE agg_tuples FINAL;

SELECT key, metrics.total_visits, metrics.unique_users FROM agg_tuples ORDER BY key;
┌─key─┬─metrics.total_visits─┬─metrics.unique_users─┐
│   1 │                  300 │                    8 │
│   2 │                   50 │                    3 │
└─────┴──────────────────────┴──────────────────────┘

total_visits 通过 sum 聚合 (100 + 200 = 300) ,而 unique_users 则通过 max 聚合 (max(5, 8) = 8) 。

Navigation