这是《ClickHouse 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 物化视图把查询结果落到目标表中,常用于明细数据实时预聚合。它是一条随写入触发的数据处理管道,不是简单的查询别名。
13.1 普通视图与物化视图
普通视图只保存 SQL:
CREATE VIEW analytics.events_view
AS
SELECT * FROM analytics.events_local
WHERE is_deleted = 0;
物化视图保存结果:
CREATE MATERIALIZED VIEW analytics.city_metric_mv
TO analytics.city_metric_local
AS
SELECT
event_date AS metric_date,
city_id,
count() AS pv,
sum(amount) AS gmv
FROM analytics.events_local
GROUP BY event_date, city_id;
普通视图查询时执行,物化视图在源表写入时更新目标表。
13.2 TO 表结构
推荐显式指定 TO 目标表:
CREATE TABLE analytics.city_metric_local
(
metric_date Date,
city_id UInt32,
pv UInt64,
gmv Decimal64(2)
)
ENGINE = SummingMergeTree
PARTITION BY toYYYYMM(metric_date)
ORDER BY (metric_date, city_id);
再创建视图:
CREATE MATERIALIZED VIEW analytics.city_metric_mv
TO analytics.city_metric_local
AS
SELECT ...
优点:
- 目标表 schema 清晰;
- 方便重建视图;
- 可以选择目标引擎;
- 支持 TTL 和分区;
- 便于备份恢复。
13.3 触发语义
物化视图由新写入的数据块触发:
INSERT block
|
+-> 写入源表
|
+-> 对该 block 执行视图 SELECT
|
v
写入目标表
这意味着:
- 视图创建前的历史数据不会自动回填;
- 每个 insert block 内的聚合范围有限;
- 目标表查询通常仍要聚合;
- 视图异常会影响写入链路;
- 删除源表数据不会自动删除目标数据。
13.4 回填历史
先暂停写入,再手动回填:
INSERT INTO analytics.city_metric_local
SELECT
event_date AS metric_date,
city_id,
count() AS pv,
sum(amount) AS gmv
FROM analytics.events_local
WHERE event_date < '2026-08-25'
GROUP BY event_date, city_id;
再创建物化视图处理新数据。关键是避免同一时间窗重复回填。
13.5 聚合状态
准确 UV 可以使用聚合状态:
CREATE TABLE analytics.city_metric_state
(
metric_date Date,
city_id UInt32,
uv_state AggregateFunction(uniq, UInt64),
pv SimpleAggregateFunction(sum, UInt64)
)
ENGINE = AggregatingMergeTree
ORDER BY (metric_date, city_id);
物化视图:
CREATE MATERIALIZED VIEW analytics.city_metric_state_mv
TO analytics.city_metric_state
AS
SELECT
event_date AS metric_date,
city_id,
uniqState(user_id) AS uv_state,
count() AS pv
FROM analytics.events_local
GROUP BY event_date, city_id;
查询:
SELECT city_id, uniqMerge(uv_state) AS uv, sum(pv) AS pv
FROM analytics.city_metric_state
GROUP BY city_id;
13.6 常见用途
| 用途 | 示例 |
|---|---|
| 预聚合 | 明细转日汇总 |
| 数据清洗 | 过滤脏数据、补默认值 |
| 维度补齐 | Lookup 后写入宽表 |
| 多目标分发 | 一份明细写多个汇总 |
| 指标状态 | uniqState、quantileState |
| 格式转换 | JSON 转结构化列 |
13.7 视图链路
events_local
|
+-> mv_daily -> daily_metrics
|
+-> mv_city -> city_metrics
|
+-> mv_realtime -> realtime_metrics
多条视图会同时消耗写入线程资源。要控制视图数量、目标表写入频率、聚合复杂度、失败重试策略和延迟监控。
13.8 管理操作
查看:
SELECT database, name, engine, as_select
FROM system.tables
WHERE is_materialized_view = 1;
删除:
DROP VIEW analytics.city_metric_mv;
物化视图定义修改通常通过删除重建完成。操作前要确认写入暂停窗口和目标表数据边界。
13.9 一致性
物化视图不提供跨表事务保证,可能出现源表可见而目标表暂不可见的情况。
排查角度:
- 写入时间;
- 视图创建时间;
- 分区边界;
- 历史回填重复;
- 目标表合并状态;
- 视图异常日志。
对账源表:
SELECT count(), sum(amount)
FROM analytics.events_local
WHERE event_date = '2026-08-25';
对账目标表:
SELECT sum(pv), sum(gmv)
FROM analytics.city_metric_local
WHERE metric_date = '2026-08-25';
13.10 反模式
| 反模式 | 问题 |
|---|---|
| 用视图替代所有模型设计 | 链路难维护 |
| 视图中做复杂 JOIN | 写入放大 |
| 目标表无 TTL | 数据无限增长 |
| 忽略 block 内聚合语义 | UV 等指标不准 |
| 频繁删除重建 | 数据边界混乱 |
| 不监控视图异常 | 静默丢数据 |
本章小结
物化视图是写入触发的实时加工管道,适合预聚合、清洗和多目标分发。推荐使用 TO 表并显式管理回填边界。聚合状态能处理去重类指标,但不能把物化视图当作事务一致的实时副本。
思考题
- 物化视图何时触发?
- 为什么历史数据需要手动回填?
- uniqState 和 uniqMerge 如何配合?
- 物化视图有哪些一致性限制?
- 如何对账源表和汇总表?