ClickHouse 物化视图实战指南

对比增量物化视图与可刷新物化视图的适用场景,提供 SummingMergeTree 与 AggregatingMergeTree 的完整建表范式

袋鼠云
袋鼠云
经营分析 数据资产门户

ClickHouse 的物化视图与其他数据库的物化视图有本质区别:它不是一个定期刷新的查询快照,而是一个写入时触发的转换器。每当数据被插入源表,物化视图会立即对这批数据执行预定义的查询,并将结果写入目标表。这种"将计算从查询时转移到写入时"的机制,能够在不增加查询延迟的前提下,为高频聚合查询提供数量级的性能提升。本文对比增量物化视图与可刷新物化视图的适用场景,并提供 SummingMergeTree 和 AggregatingMergeTree 的完整建表范式。

增量物化视图:写入时计算

增量物化视图是 ClickHouse 中最常用的物化视图类型。它的核心原理是:每次有新数据插入源表时,物化视图只对这批新数据执行查询,将结果写入目标表。目标表使用特殊的 MergeTree 家族引擎(如 SummingMergeTree 或 AggregatingMergeTree),能够在后台合并过程中自动更新聚合结果。

这种设计的关键优势在于:查询时读取的数据量大幅减少。假设原始表有 2.4 亿行投票数据,经过物化视图按天聚合后,目标表只需维护约 5000 行。查询从扫描全表变为扫描几千行,性能提升可达 25 倍以上。

SummingMergeTree:简单求和场景

SummingMergeTree 是最简单的聚合引擎,它会将所有具有相同排序键的行合并为一行,并对数值列进行求和。适合日PV统计、计数累加等场景。

以统计每日投票数为例,目标表的建表语句如下:

CREATE TABLE up_down_votes_per_day
(
    `Day` Date,
    `UpVotes` UInt32,
    `DownVotes` UInt32
)
ENGINE = SummingMergeTree
ORDER BY Day

物化视图定义如下:

CREATE MATERIALIZED VIEW up_down_votes_per_day_mv TO up_down_votes_per_day AS
SELECT
    toStartOfDay(CreationDate)::Date AS Day,
    countIf(VoteTypeId = 2) AS UpVotes,
    countIf(VoteTypeId = 3) AS DownVotes
FROM votes
GROUP BY Day

这里的关键是 TO 子句——它指定结果写入的目标表。物化视图本身不存储数据,它只是一个触发器。查询目标表时,由于后台合并尚未完成,可能存在同一日期的多行数据。此时有两种处理方式:使用 FINAL 修饰符强制合并,或者按排序键分组后手动求和。后者通常更高效,因为它允许查询优化器利用索引。

AggregatingMergeTree:复杂聚合场景

当聚合函数不是简单的求和时,需要使用 AggregatingMergeTree。它存储的是部分聚合状态(partial aggregate state),而不是最终结果。查询时需要使用对应的 Merge 后缀函数来合并这些状态。

以计算每日第 99.9 百分位数和平均评论数为例,目标表需要使用 AggregateFunction 类型:

CREATE TABLE post_stats_per_day
(
    `Day` Date,
    `Score_quantiles` AggregateFunction(quantile(0.999), Int32),
    `AvgCommentCount` AggregateFunction(avg, UInt8)
)
ENGINE = AggregatingMergeTree
ORDER BY Day

物化视图中的聚合函数需要加上 State 后缀,返回部分聚合状态而非最终结果:

CREATE MATERIALIZED VIEW post_stats_mv TO post_stats_per_day AS
SELECT
    toStartOfDay(CreationDate) AS Day,
    quantileState(0.999)(Score) AS Score_quantiles,
    avgState(CommentCount) AS AvgCommentCount
FROM posts_null
GROUP BY Day

查询时使用 Merge 后缀函数合并状态:

SELECT
    Day,
    quantileMerge(0.999)(Score_quantiles),
    avgMerge(AvgCommentCount)
FROM post_stats_per_day
GROUP BY Day
ORDER BY Day DESC
LIMIT 10

需要注意:GROUP BY 子句必须与目标表的 ORDER BY 保持一致。如果两者不匹配,可能导致合并失败或结果不正确。这是使用 AggregatingMergeTree 时最常见的错误。

可刷新物化视图:定时全量计算

可刷新物化视图是 ClickHouse 较新引入的功能,它更接近传统数据库的物化视图概念——定期在完整数据集上执行查询并刷新结果。适合需要复杂 JOIN、无法通过增量方式实现的场景。

可刷新物化视图的定义方式如下:

CREATE MATERIALIZED VIEW refreshable_mv
REFRESH EVERY 1 HOUR
AS SELECT ...

与增量物化视图的核心区别在于:可刷新物化视图支持完整的 JOIN 操作,因为它是对全量数据执行查询;而增量物化视图中的 JOIN 只会关联右侧表的快照数据,右侧表的更新不会触发视图刷新。

选择建议:如果聚合逻辑可以通过 SummingMergeTree 或 AggregatingMergeTree 实现,优先使用增量物化视图,因为它的延迟更低、资源消耗更小。只有在需要复杂 JOIN、窗口函数或其他增量方式无法实现的计算时,才考虑可刷新物化视图。

数栈·多模态数据智能平台

袋鼠云面向 AI 时代打造的多模态数据智能平台,一站式覆盖数据存算、开发、治理、资产、服务与指标、分析、AI 应用,构建可见、可用、可管、可理解的私域数据底座。

了解产品

Null 引擎的妙用

在物化视图中,有时我们只需要计算聚合结果,不需要保留原始数据。此时可以使用 Null 引擎作为"虚拟源表"——数据写入时会被丢弃,但物化视图的触发器仍然会执行。

CREATE TABLE posts_null AS posts ENGINE = Null

这种模式特别适合两种场景:一是只需要聚合结果而不需要原始明细数据的情况,可以节省大量存储空间;二是需要对同一份数据应用多个不同的物化视图时,可以将多个视图挂载到同一个 Null 表上。

级联物化视图

物化视图可以串联使用,形成多级处理流水线。第一级物化视图对原始数据进行初步聚合,第二级视图在此基础上进行更深层次的计算。这种模式适合需要多层汇总的场景,例如先按天聚合,再按月汇总。

设计级联视图时需要注意:每一级视图都有写入时的计算开销。过多的级联层数会显著增加写入延迟。建议控制在 2-3 层以内,并对每一层的写入性能进行基准测试。

常见陷阱与最佳实践

ORDER BY 与 GROUP BY 不对齐。 这是最常见的错误。使用 SummingMergeTree 或 AggregatingMergeTree 时,物化视图查询中的 GROUP BY 列必须与目标表的 ORDER BY 列完全一致。不一致会导致后台合并效率低下,甚至产生错误的查询结果。

JOIN 触发的写入延迟。 在增量物化视图中使用 JOIN 会显著增加写入延迟,因为每次插入都需要关联右侧表。优化方法是使用子查询中的 IN 来限制右侧表的扫描范围,利用左侧表的插入数据作为过滤条件,大幅减少 JOIN 的数据量。

高频写入下的合并压力。 如果写入频率很高(每秒数千次插入),后台合并可能跟不上写入速度,导致目标表中积累了大量未合并的行。此时查询性能会下降。解决方案是增大目标表的写入批次大小,或者调整 MergeTree 的合并参数。

避免在物化视图中使用 Final。 虽然 FINAL 修饰符可以保证查询结果的正确性,但它的性能开销很大,需要在查询时实时合并所有未处理的数据。建议在查询中使用 GROUP BY 替代 FINAL,让查询优化器利用排序键来加速聚合。

经营分析

围绕目标、预算、执行、异常与归因,构建经营分析、过程监控、问题定位与决策闭环。

了解解决方案

高级技巧:利用物化视图实现数据路由与过滤

物化视图不仅用于聚合加速,还可以作为数据路由和过滤的工具。在写入源表时,物化视图可以将满足特定条件的数据路由到不同的目标表,实现数据的自动分类存储。

例如,一个日志表需要按级别分为 Info 日志和 Error 日志两张表。可以创建两个物化视图,分别过滤不同级别的日志并写入对应的目标表。这种方式的优点是:写入逻辑保持简洁(只需要向源表写入),数据路由的逻辑封装在物化视图中,对应用层完全透明。

另一个常见场景是列裁剪。如果下游查询只需要原始表中的部分列,可以通过物化视图在写入时只保留必要的列,丢弃不需要的字段。这不仅减少了目标表的存储空间,还简化了查询逻辑。

性能基准测试方法

在生产环境部署物化视图之前,建议进行充分的性能基准测试。测试应覆盖两个维度:写入性能和查询性能。

对于写入性能,使用与生产环境相当的数据量和写入频率进行测试,记录每秒插入的行数和延迟。如果物化视图中包含 JOIN 操作,需要特别关注右侧表大小对写入延迟的影响。一个实用的方法是逐步增加右侧表的数据量,观察写入延迟的变化趋势。

对于查询性能,使用实际的业务查询对物化视图目标表和原始表分别执行,对比两者的耗时和扫描的数据量。同时检查目标表的行合并状态——如果未合并的行数过多,查询性能可能会退化。定期执行 OPTIMIZE TABLE ... FINAL 可以强制触发合并,但会消耗 IO 资源。

物化视图是 ClickHouse 查询加速的核心工具。理解增量计算和全量刷新两种模式的差异,选择合适的聚合引擎,并遵循 ORDER BY 与 GROUP BY 对齐的原则,就能构建出高效且可靠的预计算层。

开启Data+AI 之旅

填写表单,获取专属解决方案

400-002-1024

工作日 9:00-18:00

关注公众号

获取最新产品动态与技术干货

关注公众号

企业微信

扫码添加企业微信