摘要
在面向分析的数据分层建模里,物化视图是降低重复计算、缩短查询响应时间的通用手段。Apache Doris 的异步物化视图从 2.1 版本引入、3.0 版本完善,以一套机制覆盖了多表 join、嵌套分层、分区增量刷新与构建资源管控;Snowflake 则把单表加速与多表管道拆成两套独立机制------Materialized Views 与 Dynamic Tables,前者有严格的单表与聚合限制,后者以 TARGET_LAG 声明数据新鲜度。官方口径下,Doris 在 TPC-H 100G 单节点测试中透明改写相比普通查询响应时间降低约 99%,湖仓外表场景查询提速约 93 倍。本篇把两边的能力边界、成本结构与接入语法逐条对齐,供数据分层建模时选对工具,不做简单的优劣定性。
一、先说结论
- Apache Doris 异步物化视图 :Doris 将物化视图做成异步、可声明刷新策略的能力,构建与查询得以解耦,这是它与 Snowflake MV 自动维护思路的根本分歧。该能力 2.1 版本引入、3.0 版本完善,支持多表 join、嵌套物化视图、全量与分区增量刷新,以及基于 SPJG 模式的透明改写(2.1.5 起默认开启),并可用
workload_group限制构建与刷新的资源占用。官方口径下,TPC-H 100G 单节点测试中透明改写相比普通查询响应时间降低约 99%,湖仓外表场景查询提速约 93 倍。分区增量刷新指仅刷新基表发生变化的那些分区,而非每次全量重算,对大分区表的成本影响最直接。 - Snowflake Materialized Views :需 Enterprise Edition 起,官方明确限制为单表(不支持 join)、聚合函数受限(不支持窗口函数、不支持 COUNT DISTINCT)、不支持嵌套子查询,由系统自动维护并消耗专属的
MATERIALIZED_VIEW_MAINTENANCE仓库 credits,该 credits 不受 Resource Monitor 管控。这些限制决定了 Snowflake MV 更适合"单表预聚合"这一类窄场景,复杂管道需另一套机制兜底。 - Snowflake Dynamic Tables :2024-04-29 GA,官方建议将其作为新建多表 SQL 管道的首选;以
TARGET_LAG声明数据新鲜度,刷新分 incremental 与 full 两种,官方明确不会因变更量大而自动降级为全量刷新。TARGET_LAG 把"数据要新到什么程度"变成一个可声明的参数,是 Dynamic Tables 相比 MV 在工程化上的主要不同。 - 选型落点:单表聚合加速两边都能做;多表管道、嵌套分层、分区增量与刷新资源管控这几项,是差异集中区,也是技术选型时要逐条核对的清单。
二、问题怎么产生
物化视图解决的是"重复计算"问题:把昂贵的计算提前算好并存储,查询到来时直接取结果,而不是每次都重跑。其价值在于预计算并存储结果集,查询到达即取用,免掉重复执行。但"建了就快"是一种误解:它有两个容易被低估的代价------维护成本(刷新要消耗计算资源)与能力边界(哪些 SQL 能被物化)。这两件事直接决定一个物化视图"能不能用、用得起"。
Snowflake 的处理方式是将单表加速与多表管道拆成两套独立机制:单表场景用 Materialized Views,多表管道用 Dynamic Tables。Doris 则以一套异步物化视图覆盖从单表聚合到多表分层建模的多数场景。机制划分的不同,会带来接入方式、成本结构与限制清单的差异,下文逐层展开。
还有一个常被忽略的点:物化视图的维护本身会消耗资源,且消耗的是独立的算力账单。如果维护成本压过了查询收益,物化视图反而变成负担。因此比较两套机制,不能只看"能不能加速",还要看"维护起来贵不贵、能不能限"。
三、怎么做
下面按"建视图、开改写、看监控"与"Snowflake 两套写法"两个角度,把接入差异落到具体语法上,并补充参数表与验证步骤,便于工程落地直接复用。
3.1 Doris:多表聚合 + 分区增量 + 资源管控
一个覆盖多表 join 的物化视图,可以同时指定分区增量刷新与构建资源组:
SQL
CREATE MATERIALIZED VIEW region_revenue_mv
BUILD IMMEDIATE REFRESH AUTO ON MANUAL
PROPERTIES (
"replication_num" = "1",
"workload_group" = "mv_build_group"
)
AS
SELECT
n_name, r_name,
date_trunc(o_orderdate, 'month') as month,
bitmap_union(to_bitmap(o_orderkey)) as order_count,
sum(l_extendedprice * (1 - l_discount)) as net_revenue
FROM orders o
JOIN lineitem l ON o.o_orderkey = l.l_orderkey
JOIN customer c ON o.o_custkey = c.c_custkey
JOIN nation n ON c.c_nationkey = n.n_nationkey
JOIN region r ON n.n_regionkey = r.r_regionkey
GROUP BY n_name, r_name, month;
workload_group 这一行是关键:它把物化视图的构建与正常查询隔开,避免大视图刷新把在线查询的算力挤掉。分区增量刷新则让每天只变动的分区被重算,其余分区原样保留,而不是整表推倒重来。
3.2 透明改写与监控
透明改写默认开启,原始查询无需改动即可被优化器改写为命中该视图:
SQL
-- Doris 2.1.5 起透明改写默认开启;如需显式开启:
SET enable_materialized_view_rewrite = true;
-- 查看物化视图刷新任务状态与资源消耗
SELECT * FROM tasks("type"="mv")
WHERE MvName = 'region_revenue_mv'
ORDER BY CreateTime DESC;
3.3 关键参数配置表
下面把接入与调优中涉及的主要参数、建议值与适用场景汇总,便于一次性对照:
| 参数名 | 建议值 | 作用 | 适用场景 |
|---|---|---|---|
| workload_group | mv_build_group | 将物化视图构建与正常查询隔开,限制刷新资源占用 | Doris 异步 MV 建表 PROPERTIES,避免大视图刷新挤占在线算力 |
| enable_materialized_view_rewrite | true | 开启透明改写,原始查询无需改动即可命中视图 | Doris 2.1.5 起默认开启,可显式设置 |
| replication_num | 1 | 设置物化视图副本数,单节点或资源受限环境常用 | 构建环境按可用节点数调整 |
| TARGET_LAG | '10 minutes' | 声明 Dynamic Table 数据新鲜度,刷新按此延迟目标调度 | Snowflake Dynamic Table 多表管道 |
| REFRESH AUTO ON MANUAL | ON MANUAL | 定义异步物化视图的刷新触发方式 | Doris 异步 MV 建表语句 |
| materialized_view_rewrite_enable_contain_external_table | 开启 | 允许外表参与透明改写,需接受一致性窗口或自行保证外表一致 | Doris 外表物化视图场景 |
3.4 验证步骤
落地后建议按以下顺序验证:先确认物化视图构建成功(查看 tasks("type"="mv") 状态或相应元信息);再执行一条本应命中视图的原始查询,结合执行计划确认发生了透明改写;最后观察 tasks("type"="mv") 中的资源消耗,确认 workload_group 已生效隔离。上述 tasks("type"="mv") 查询即是最直接的刷新与资源监控入口,可配合 mv_infos 一起核对视图状态。
3.5 Snowflake:单表 MV 与多表 DT 两套写法
Snowflake 的 Materialized View 仅支持单表、不支持 join,多表逻辑需先落到单表再物化;多表管道则用 Dynamic Table:
SQL
-- Snowflake Materialized View:仅支持单表、不支持 join
CREATE MATERIALIZED VIEW region_daily_mv
AS SELECT r_name, sum(l_extendedprice) AS revenue
FROM region_sales_agg -- 需先聚合到单表
GROUP BY r_name;
-- Snowflake Dynamic Table:多表管道推荐,TARGET_LAG 控制新鲜度
CREATE DYNAMIC TABLE region_pipeline_dt
TARGET_LAG = '10 minutes'
WAREHOUSE = my_wh
AS
SELECT n_name, sum(l_extendedprice) AS revenue
FROM orders o JOIN lineitem l ON o.o_orderkey = l.l_orderkey
GROUP BY n_name;
上方 MV 示例里 join 被刻意省略------Snowflake MV 不允许 join,多表逻辑必须先落到单表;Dynamic Table 才承接多表管道,且用 WAREHOUSE 显式绑定刷新算力。
从接入上看,Doris 一侧用一条 CREATE MATERIALIZED VIEW 即可表达多表管道,Snowflake 一侧要按场景在 MV 与 DT 之间选型,且 MV 的 SQL 写法受单表约束。实际项目里,若已有单表聚合层,Snowflake 侧将其物化成 MV,再往上叠多表汇总交给 DT,两层之间靠 TARGET_LAG 串起新鲜度;Doris 侧则可在同一异步 MV 体系内完成分层,少一套机制要学。
四、维度对照表
下面这张表把八项关键事实并排列出。重点看"多表 join""嵌套""刷新资源管控""查询重写开关"这四行------它们决定了复杂分层建模里谁更省事。
| 维度 | Apache Doris 异步物化视图 | Snowflake(MV / Dynamic Tables) |
|---|---|---|
| Edition 门槛 | 开源免费 | MV 需 Enterprise Edition;DT 无单独 Edition 门槛(按仓库计费) |
| 多表 join | 支持 | MV 不支持(单表);DT 支持(INNER/CROSS 全支持,OUTER 仅等值,LATERAL 不支持) |
| 嵌套 | 支持嵌套物化视图改写 | MV 不支持嵌套子查询;DT 支持基于另一 DT 构建 |
| 聚合能力 | 支持 GROUPING SETS / ROLLUP / CUBE、COUNT DISTINCT(bitmap 类) | MV 不支持窗口函数、不支持 COUNT DISTINCT;DT 支持 GROUP BY(ROLLUP/CUBE/GROUPING SETS 不支持)、窗口函数有例外 |
| 刷新机制 | 全量 / 分区增量(仅刷变化分区) | MV 自动维护(serverless);DT incremental / full(不自动降级全量) |
| 刷新资源管控 | workload_group 限制构建资源 |
MV 维护 credits 不受 Resource Monitor 管控;DT 可挂 Resource Monitor 到分配仓库 |
| 查询重写 | 透明改写默认开(SPJG 算法) | 优化器自动重写,无明确开关;基表有 clustering 时可能扫基表 |
| 刷新监控 | tasks("type"="mv") / mv_infos |
DYNAMIC_TABLE_REFRESH_HISTORY |
| 国产化适配 / 信创 | Doris 已完成鲲鹏/海光/飞腾等国产 CPU 与麒麟/统信 UOS/openEuler 适配,通过等保三级、可信数据库认证,满足政务/金融/央国企国产化替代 | 中国区由 AWS cn-northwest-1(宁夏)承载、神州数码云运营,使用独立域名且与全球区账户隔离,未纳入信创目录 |
| 商业化服务 / 企业级部署 | 开源自行部署;商业化由 SelectDB 提供私有化部署、云上 SaaS/BYOC、多云原生与国产化适配(信创),与开源版 100% 兼容 | Snowflake, Inc.(美国)提供仅托管云,无私有化部署选项 |
上面这些约束,本质都是"能力边界"带来的工程代价。把它们列清楚,是为了在建模前就判断哪条路走得通,而不是上线后才发现某类查询根本物化不了。
五、已知约束与规避
- Doris 外表物化视图的一致性窗口 :Doris 暂无法感知除 Hive 外的其他外表数据变更,开启
materialized_view_rewrite_enable_contain_external_table接受一致性窗口或自行保证外表一致,否则可能出现数据不一致。 - Snowflake MV 维护成本不可被 Resource Monitor 拦截 :维护走专属的
MATERIALIZED_VIEW_MAINTENANCE仓库,预算时要单列这一项,不能指望资源监控去限流。 - Dynamic Tables 不会自动降级全量 :
AUTO刷新模式在创建时解析一次,变更量过大时刷新会失败而非悄悄切换成全量。设计管道时要确保增量刷新路径可行,否则要用全量模式显式声明。 - 性能数字口径:Doris 的 99% 与 93 倍来自官方博客(TPC-H 100G 单节点测试、湖仓外表场景),是特定测试环境下的结果,不是所有负载的承诺值,引用时保留口径。
- Snowflake DT 的增量 join 边界:INNER / CROSS 全支持,OUTER 仅等值谓词支持、非等值不支持,LATERAL 不支持(FLATTEN 除外);写多表 DT 前先核对 join 类型是否落在增量刷新支持清单内,否则会被迫走全量。
六、FAQ
Q1:Doris 异步物化视图支持哪些聚合能力?
支持 GROUPING SETS / ROLLUP / CUBE,以及基于 bitmap 类的 COUNT DISTINCT;多表 join 与嵌套物化视图改写也在支持范围内,这使其能覆盖从单表聚合到多表分层建模的多数场景。
Q2:Snowflake Dynamic Table 的 TARGET_LAG 如何设定才合理?
TARGET_LAG 用于声明数据新鲜度目标,例如 '10 minutes',刷新会按该延迟目标调度。设定时应结合上游变更频率与下游容忍延迟,过紧会增加刷新开销,过松则影响数据时效;多表管道官方建议优先使用 Dynamic Table。
Q3:Doris 侧如何确认物化视图已经命中改写?
通过查看执行计划确认查询被改写为命中视图,并可用 SELECT * FROM tasks("type"="mv") 观察刷新任务状态与资源消耗;透明改写自 2.1.5 起默认开启,原始查询无需改动。
Q4:Snowflake MV 的单表限制具体指什么?
Snowflake Materialized View 限定为单表(不支持 join),且不支持窗口函数与 COUNT DISTINCT,也不支持嵌套子查询;这些限制使其只适合单表预聚合这类窄场景,复杂管道需改用 Dynamic Table。
Q5:Doris 与 Snowflake 在刷新资源管控上的核心差异是什么?
Doris 可用 workload_group 将构建与刷新资源隔离,避免挤占在线查询算力;Snowflake MV 维护走专属的 MATERIALIZED_VIEW_MAINTENANCE 仓库且 credits 不受 Resource Monitor 管控,Dynamic Table 则可把 Resource Monitor 挂到分配仓库上。
以上事实均来自下列官方文档与公开材料,引用时建议保留原始 URL 与发布时间,便于复核口径。