82.【数据库】ClickHouse从入门到放弃-AggregatingMergeTree

874 阅读6分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第18天,点击查看活动详情 

文档参考:《ClickHouse原理解析与应用实践(数据库技术丛书)(朱凯)》

70.【数据库】ClickHouse从入门到放弃-MergeTree的创建方式 - 掘金 (juejin.cn)

71.【数据库】ClickHouse从入门到放弃-MergeTree的存储结构 - 掘金 (juejin.cn)

72.【数据库】ClickHouse从入门到放弃-数据分区 - 掘金 (juejin.cn)

73.【数据库】ClickHouse从入门到放弃-一级索引 - 掘金 (juejin.cn)

74.【数据库】ClickHouse从入门到放弃-二级索引 - 掘金 (juejin.cn)

75.【数据库】ClickHouse从入门到放弃-数据存储 - 掘金 (juejin.cn)

76.【数据库】ClickHouse从入门到放弃-数据标记 - 掘金 (juejin.cn)

77.【数据库】ClickHouse从入门到放弃-对于分区、索引、标记和压缩数据的协同总结 - 掘金 (juejin.cn)

78.【数据库】ClickHouse从入门到放弃-MergeTree 数据TTL - 掘金 (juejin.cn)

79.【数据库】ClickHouse从入门到放弃-MergeTree 多路径存储策略 - 掘金 (juejin.cn)

80.【数据库】ClickHouse从入门到放弃-ReplacingMergeTree - 掘金 (juejin.cn)

81.【数据库】ClickHouse从入门到放弃-SummingMergeTree - 掘金 (juejin.cn)

1.AggregatingMergeTree

有过数据仓库建设经验的读者一定知道“数据立方体”的概念,这是一个在数据仓库领域十分常见的模型。它通过以空间换时间的方法提升查询性能,将需要聚合的数据预先计算出来,并将结果保存起来。在后续进行聚合查询的时候,直接使用结果数据。

AggregatingMergeTree就有些许数据立方体的意思,它能够在合并分区的时候,按照预先定义的条件聚合数据。同时,根据预先定义的聚合函数计算数据并通过二进制的格式存入表内。将同一分组下的多行数据聚合成一行,既减少了数据行,又降低了后续聚合查询的开销。可以说,AggregatingMergeTree是SummingMergeTree的升级版,它们的许多设计思路是一致的,例如同时定义ORDER BY与PRIMARY KEY的原因和目的。但是在使用方法上,两者存在明显差异,应该说AggregatingMergeTree的定义方式是MergeTree家族中最为特殊的一个。

声明使用AggregatingMergeTree的方式如下:

ENGINE = AggregatingMergeTree()

AggregatingMergeTree没有任何额外的设置参数,在分区合并时,在每个数据分区内,会按照ORDER BY聚合。而使用何种聚合函数,以及针对哪些列字段计算,则是通过定义AggregateFunction数据类型实现的。以下面的语句为例:

CREATE TABLE agg_table(
    id String,
    city String,
    code AggregateFunction(uniq,String),
    value AggregateFunction(sum,UInt32),
    create_time DateTime
)ENGINE = AggregatingMergeTree()
PARTITION BY toYYYYMM(create_time)
ORDER BY (id,city)
PRIMARY KEY id

上例中列字段id和city是聚合条件,等同于下面的语义:

GROUP BY id,city

而code和value是聚合字段,其语义等同于: AggregateFunction是ClickHouse提供的一种特殊的数据类型,它能够以二进制的形式存储中间状态结果。其使用方法也十分特殊,对于AggregateFunction类型的列字段,数据的写入和查询都与寻常不同。在写入数据时,需要调用State函数;而在查询数据时,则需要调用相应的Merge函数。其中,*表示定义时使用的聚合函数。例如示例中定义的code和value,使用了uniq和sum函数:

code AggregateFunction(uniq,String),
value AggregateFunction(sum,UInt32),

那么,在写入数据时需要调用与uniq、sum对应的uniqState和sumState函数,并使用INSERT SELECT语法:

INSERT INTO TABLE agg_table 
SELECT 'A000','wuhan',
uniqState('code1'),
sumState(toUInt32(100)),
'2019-08-10 17:00:00'

在查询数据时,如果直接使用列名访问code和value,将会是无法显示的二进制形式。此时,需要调用与uniq、sum对应的uniqMerge、sumMerge函数:

SELECT id,city,uniqMerge(code),sumMerge(value) FROM agg_table 
GROUP BY id,city

讲到这里,你是否会认为AggregatingMergeTree使用起来过于烦琐了?连正常进行数据写入都需要借助INSERT…SELECT的句式并调用特殊函数。如果直接像刚才示例中那样使用AggregatingMergeTree,确实会非常麻烦。不过各位读者并不需要忧虑,因为目前介绍的这种使用方法,并不是它的主流用法。

AggregatingMergeTree更为常见的应用方式是结合物化视图使用,将它作为物化视图的表引擎。而这里的物化视图是作为其他数据表上层的一种查询视图,如图7-1所示。

现在用一组示例说明。首先,建立明细数据表,也就是俗称的底表:

CREATE TABLE agg_table_basic(
    id String,
    city String,
    code String,
    value UInt32
)ENGINE = MergeTree()
PARTITION BY city
ORDER BY (id,city)

image.png

通常会使用MergeTree作为底表,用于存储全量的明细数据,并以此对外提供实时查询。接着,新建一张物化视图:

CREATE MATERIALIZED VIEW agg_view
ENGINE = AggregatingMergeTree() 
PARTITION BY city
ORDER BY (id,city)
AS SELECT
    id,
    city,
    uniqState(code) AS code,
    sumState(value) AS value
FROM agg_table_basic
GROUP BY id, city

物化视图使用AggregatingMergeTree表引擎,用于特定场景的数据查询,相比MergeTree,它拥有更高的性能。

在新增数据时,面向的对象是底表MergeTree:

INSERT INTO TABLE agg_table_basic 
VALUES('A000','wuhan','code1',100),('A000','wuhan','code2',200),('A000','zhuhai', 'code1',200)

数据会自动同步到物化视图,并按照AggregatingMergeTree引擎的规则处理。 在查询数据时,面向的对象是物化视图AggregatingMergeTree:


SELECT id, sumMerge(value), uniqMerge(code) FROM agg_view GROUP BY id, city
┌─id──┬─sumMerge(value)──┬──uniqMerge(code)─┐
│ A000  │             2001     │
│ A000  │             3002     │
└─────┴────────────┴────────────┘

接下来,简单梳理一下AggregatingMergeTree的处理逻辑。

(1)用ORBER BY排序键作为聚合数据的条件Key。 (2)使用AggregateFunction字段类型定义聚合函数的类型以及聚合的字段。 (3)只有在合并分区的时候才会触发聚合计算的逻辑。 (4)以数据分区为单位来聚合数据。当分区合并时,同一数据分区内聚合Key相同的数据会被合并计算,而不同分区之间的数据则不会被计算。 (5)在进行数据计算时,因为分区内的数据已经基于ORBER BY排序,所以能够找到那些相邻且拥有相同聚合Key的数据。 (6)在聚合数据时,同一分区内,相同聚合Key的多行数据会合并成一行。对于那些非主键、非AggregateFunction类型字段,则会使用第一行数据的取值。 (7)AggregateFunction类型的字段使用二进制存储,在写入数据时,需要调用State函数;而在查询数据时,则需要调用相应的Merge函数。其中,*表示定义时使用的聚合函数。 (8)AggregatingMergeTree通常作为物化视图的表引擎,与普通MergeTree搭配使用。