MaxCompute新推出动态物化视图功能(Delta Live Materialized View),帮助用户构建简单易用的增量更新Pipeline。本文介绍MaxCompute动态物化视图的相关操作。
功能介绍
动态物化视图相比全量刷新物化视图,能够平衡数据新鲜度和计算代价,充分利用存量的计算结果,通过智能的增量计算算法,减少计算代价,并提高数据新鲜度。
该功能目前处于邀测中,邀测功能使用方法请参见使用说明。
整体架构

核心优势
MaxCompute的动态物化视图Delta Live MV功能具备以下优势:
-
声明式SQL、免运维、自动数仓分层。
-
简化数仓架构,一份计算逻辑,一个引擎,同时支持增量计算和全量计算,同时满足低延迟和高吞吐需求。
-
成本效益,平衡数据新鲜度和成本,高效处理增全量ETL。
适用场景
动态物化视图功能适用于以下场景:
-
离线业务实时化
从T+1数仓演进到分钟级近实时数仓。
-
增全量一体
-
当天分区近实时增量计算:业务数据新鲜度要求高,计算性价比高。
-
(可选操作)历史分区回刷:数据归档、修正,用于后续大规模数据分析。
-
-
全面支持各种SQL逻辑的增量计算,包含以下常见的SQL算子:
-
双流INNER JOIN
-
双流(LEFT/RIGHT)OUTER JOIN
-
任意AGGREGATE(除UDAF外),包括无GROUP BY、无AGG的Function。
-
WINDOW
-
TableFunctionScan
-
UNION ALL
-
FILTER/Project
-
SUBQUERY
-
注意事项
-
源表(source table)已开启CDC功能。目前支持的源表类型如下:
-
Delta Table增量表,并且需要开启CDC功能。
-
动态物化视图表。动态物化视图默认已开启CDC功能。
-
-
动态物化视图不能包含非确定性计算,如RAND函数、UDF等。
创建动态物化视图
语法格式
CREATE MATERIALIZED VIEW [IF NOT EXISTS][<project_name>.]<mv_name>
[LIFECYCLE <days>] --指定生命周期
[BUILD DEFERRED] -- 指定是在创建时只生成表结构,不生成数据
[(<col_name> [COMMENT <col_comment>],...)] --列注释
[DISABLE REWRITE] --指定是否用于改写
[COMMENT <table comment>] --表注释
[PARTITIONED ON/BY (<col_name> [, <col_name>, ...]) --创建物化视图表为分区表
[REFRESH EVERY <num> MINUTES/HOURS/DAYS] -- 设置物化视图定时更新间隔
TBLPROPERTIES(
"refresh_mode"="incremental"
[,"enable_auto_refresh"="true"] --指定是否开启自动刷新
[,"refresh_cron"="xx"] --根据cron定时器,可以配置定时间隔更新或者定点更新,间隔定点更新等
[,"refresh_job_settings"="xx"]
)
AS <select_statement>;
动态物化视图Delta Live MV的语法兼容主要普通物化视图的语法,目前存在以下差异:
-
当前不支持将动态物化视图创建为聚簇表。
-
动态物化视图不支持将
enable_auto_substitute参数设置为true。动态物化视图是异步物化视图形态,所使用的base table数据不一定是最新版本,与enable_auto_substitute设置为true时冲突。
参数说明
|
参数 |
是否必填 |
描述 |
|
project_name |
否 |
项目名称。 |
|
mv_name |
是 |
Delta Live MV名称。 |
|
LIFECYCLE <days> |
否 |
指定生命周期。 |
|
BUILD DEFERRED |
否 |
指定在创建时只生成表结构,不生成数据。 |
|
col_name |
否 |
列名。 |
|
col_comment |
否 |
列注释。 |
|
DISABLE REWRITE |
否 |
指定是否用于改写。 |
|
table comment |
否 |
表注释。 |
|
REFRESH EVERY <num> MINUTES/HOURS/DAYS |
否 |
指定刷新的调度间隔,最小值为1分钟。 |
|
enable_auto_refresh |
否 |
是否开启自动刷新。
|
|
refresh_mode |
否 |
刷新模式。
|
|
refresh_cron |
否 |
指定Cron表达式以设置刷新频率,可配置定时间隔更新、定点更新或间隔定点更新等。 取值为QUARTZ Cron格式的字符串,使用详情请参见Cron expression examples。示例如下:
|
|
refresh_job_settings |
否 |
|
|
select_statement |
是 |
SQL查询语句。 |
使用示例
示例1:创建简单动态物化视图
定义一个名为mv1的动态物化视图,每5分钟自动进行一次增量刷新。其中source为一张开启CDC功能的Delta Table。
CREATE MATERIALIZED VIEW IF NOT EXISTS mv1
REFRESH EVERY 5 MINUTES
TBLPROPERTIES("enable_auto_refresh"="true", "refresh_mode"="incremental")
AS
SELECT name, COUNT(*) FROM source GROUP BY name;
示例2:创建含通用调优参数的动态物化视图
SET odps.task.major.version=sql_flighting_dlmv;
CREATE MATERIALIZED VIEW IF NOT EXISTS part_dlmv_department
PRIMARY KEY(dept_id) -- 可以通过SQL逻辑推导出pk列为dept_id(通过base表的pk列推导出来),
-- 预期不需要显示声明,但由于分区mv目前只支持BUILD DEFERRED,
-- 该模式下还没有做pk推导的功能,所以需要显示的声明pk
LIFECYCLE 10
BUILD DEFERRED
PARTITIONED BY (pt)
TBLPROPERTIES('refresh_mode'='incremental',
'refresh_job_settings'='set odps.task.major.version=sql_flighting_dlmv;')
AS
SELECT *, get_setting('odps.custom.setting.department.pt') AS pt FROM t_department;
MV 刷新的主键要求:
目前MV刷新要求 MV 本身是一张PK(主键)表。区别于PK Delta Table,MV 由一段SQL逻辑产生。创建 MV 时是否需要显式声明 Primary Key,取决于以下情况:
-
SQL逻辑可自动推导出PK时
例如,SQL 中包含 GROUP BY key,则 MV 的主键自动推导为 key,无需显式声明。可通过
DESC EXTENDED mvName;命令查看推导出的主键列。 -
SQL逻辑无法自动推导出主键时
-
方式一:修改 MV 的 SQL 逻辑,添加 GROUP BY 使其满足自动推导条件。
-
方式二:显式声明 Primary Key。需要注意,数据本身必须满足唯一性约束。 系统会尽力进行数据唯一性校验,即每次增量刷新时均会执行唯一性检查,初始化阶段目前尚有优化空间,后续将尽快补充主键唯一性检查能力。
-
-
分区 MV 的特殊要求
分区 MV 当前仅支持 BUILD DEFERRED模式,该模式下暂不支持主键自动推导,因此必须显式声明 Primary Key。
创建分区DLMV
场景一:使用分区 DLMV 表达当天入仓的增量数据以及历史的全量数据
SET odps.task.major.version=sql_flighting_dlmv;
CREATE MATERIALIZED VIEW IF NOT EXISTS part_dlmv_department
PRIMARY KEY(dept_id) -- 可以通过SQL逻辑推导出pk列为dept_id(通过base表的pk列推导出来),
-- 预期不需要显示声明,但由于分区mv目前只支持BUILD DEFERRED,
-- 该模式下还没有做pk推导的功能,所以需要显示的声明pk
LIFECYCLE 10
BUILD DEFERRED
PARTITIONED BY (pt)
TBLPROPERTIES('refresh_mode'='incremental',
'refresh_job_settings'='set odps.task.major.version=sql_flighting_dlmv;')
AS
-- t_department 是近实时入仓表,主要描述用户当天的增量数据
SELECT *, get_setting('odps.custom.setting.department.pt') AS pt FROM t_department;
UNION ALL
-- history_t_department 是历史分区表,包含了历史所有数据
SELECT * FROM history_t_department;
场景二:通过group by key推导出dlmv的pk列。
// pk delta table
CREATE TABLE dlmv_base_table(
key STRING NOT NULL PRIMARY KEY,
value BIGINT,
value2 BIGINT
)
STORED AS ALIORC
TBLPROPERTIES (
'transactional' = 'true',
'cdc.insert.into.passthrough.enable' = 'true',
'acid.cdc.mode.enable' = 'true',
'acid.cdc.build.async' = 'false'
);
CREATE MATERIALIZED VIEW dlmv_pt
PRIMARY KEY(value) -- 可以通过SQL逻辑推导出pk列为value(gropy by value),
-- 预期不需要显示声明pk列,但分区dlmv当前只支持BUILD DEFERRED模式,
-- 该模式下还没有做pk推导的功能,所以需要显示的声明pk列
build deferred
partitioned BY (pt)
TBLPROPERTIES (
'refresh_mode' = 'incremental',
'enable_auto_refresh' = 'true'
)
AS SELECT *, get_setting('odps.custom.setting.dlmv_pt.pt') AS pt FROM (SELECT value, MAX(value2) FROM dlmv_base_table GROUP BY value) t;
场景三:通过base表的pk推导出dlmv的pk列。
// pk delta table
CREATE TABLE dlmv_base_table(
key STRING NOT NULL PRIMARY KEY,
value BIGINT,
value2 BIGINT
)
STORED AS ALIORC
TBLPROPERTIES (
'transactional' = 'true',
'cdc.insert.into.passthrough.enable' = 'true',
'acid.cdc.mode.enable' = 'true',
'acid.cdc.build.async' = 'false'
);
CREATE MATERIALIZED VIEW dlmv_pt
PRIMARY KEY(key) --可以通过SQL逻辑推导出pk列为key(通过base表的pk列推导出来),预期不需要显示声明pk列, 但分区dlmv当前只支持BUILD DEFERRED模式,该模式下还没有做pk推导的功能,所以需要显示的声明pk列
build deferred
partitioned BY (pt)
TBLPROPERTIES (
'refresh_mode' = 'incremental',
'enable_auto_refresh' = 'true'
)
AS
SELECT key, value, value2, get_setting('odps.custom.setting.dlmv_pt.pt') as pt FROM dlmv_base_table;
场景四:无法推导出dlmv的pk列。
// pk delta table
CREATE TABLE dlmv_base_table(
key STRING NOT NULL PRIMARY KEY,
value BIGINT,
value2 BIGINT
)
STORED AS ALIORC
TBLPROPERTIES (
'transactional' = 'true',
'cdc.insert.into.passthrough.enable' = 'true',
'acid.cdc.mode.enable' = 'true',
'acid.cdc.build.async' = 'false'
);
# 1. 直接显示声明pk列
CREATE MATERIALIZED VIEW dlmv_pt
primary key(value) -- 通过SQL逻辑推导出不出来pk列为valu,但用户侧知道数据本身是满足pk唯一性的, 需要显示声明pk列
build deferred
partitioned BY (pt)
TBLPROPERTIES (
'refresh_mode' = 'incremental',
'enable_auto_refresh' = 'true'
)
AS
SELECT value, value2, get_setting('odps.custom.setting.dlmv_pt.pt') as pt FROM dlmv_base_table;
# 2. 改造SQL逻辑,从而能推导出pk列
-- 如果用户侧无法保证value列值的唯一性,则可以通过group by逻辑进行改造,如下所示:
CREATE MATERIALIZED VIEW dlmv_pt
primary key(value) -- 可以通过SQL逻辑推导出pk列为value(gropy by value),预期不需要显示声明pk列,但分区dlmv当前只支持BUILD DEFERRED模式,该模式下还没有做pk推导的功能,所以需要显示的声明pk列
build deferred
partitioned BY (pt)
TBLPROPERTIES (
'refresh_mode' = 'incremental',
'enable_auto_refresh' = 'true'
)
AS SELECT value, MAX(value2), get_setting('odps.custom.setting.dlmv_pt.pt') as pt FROM dlmv_base_table GROUP BY value;
创建非分区DLMV
场景一:通过group by key推导出dlmv的pk列。
// pk delta table
CREATE TABLE dlmv_base_table(
key STRING NOT NULL PRIMARY KEY,
value BIGINT,
value2 BIGINT
)
STORED AS ALIORC
TBLPROPERTIES (
'transactional' = 'true',
'cdc.insert.into.passthrough.enable' = 'true',
'acid.cdc.mode.enable' = 'true',
'acid.cdc.build.async' = 'false'
);
CREATE MATERIALIZED VIEW dlmv
-- primary key(value) 可以通过SQL逻辑推导出pk列为value(gropy by value),不需要显示声明pk列
TBLPROPERTIES (
'refresh_mode' = 'incremental',
'enable_auto_refresh' = 'true'
)
AS SELECT value, MAX(value2) FROM dlmv_base_table GROUP BY value;
场景二:通过base表的pk推导出dlmv的pk列。
// pk delta table
CREATE TABLE dlmv_base_table(
key STRING NOT NULL PRIMARY KEY,
value BIGINT,
value2 BIGINT
)
STORED AS ALIORC
TBLPROPERTIES (
'transactional' = 'true',
'cdc.insert.into.passthrough.enable' = 'true',
'acid.cdc.mode.enable' = 'true',
'acid.cdc.build.async' = 'false'
);
CREATE MATERIALIZED VIEW dlmv
-- primary key(key) 可以通过SQL逻辑推导出pk列为key(通过base表的pk列推导出来),不需要显示声明pk列
TBLPROPERTIES (
'refresh_mode' = 'incremental',
'enable_auto_refresh' = 'true'
)
AS
SELECT key, value, value2 FROM dlmv_base_table;
场景三:无法推导出dlmv的pk列。
// pk delta table
CREATE TABLE dlmv_base_table(
key STRING NOT NULL PRIMARY KEY,
value BIGINT,
value2 BIGINT
)
STORED AS ALIORC
TBLPROPERTIES (
'transactional' = 'true',
'cdc.insert.into.passthrough.enable' = 'true',
'acid.cdc.mode.enable' = 'true',
'acid.cdc.build.async' = 'false'
);
# 1. 显示声明pk列
CREATE MATERIALIZED VIEW dlmv
primary key(value) -- 通过SQL逻辑推导出不出来pk列为valu,但用户侧知道数据本身是满足pk唯一性的, 需要显示声明pk列
TBLPROPERTIES (
'refresh_mode' = 'incremental',
'enable_auto_refresh' = 'true'
)
AS
SELECT value, value2 FROM dlmv_base_table;
# 2. 改造SQL逻辑,从而能推导出pk列
-- 如果用户侧无法保证value列值的唯一性,则可以通过group by逻辑进行改造,如下所示:
CREATE MATERIALIZED VIEW dlmv
-- primary key(value) 可以通过SQL逻辑推导出pk列为value(gropy by value),不需要显示声明pk列
TBLPROPERTIES (
'refresh_mode' = 'incremental',
'enable_auto_refresh' = 'true'
)
AS SELECT value, MAX(value2) FROM dlmv_base_table GROUP BY value;
示例3:创建单分区增量刷新的动态物化视图
创建分区动态物化视图时,需添加BUILD DEFERRED关键字,表示仅执行DDL操作。
-- 创建动态物化视图
CREATE MATERIALIZED VIEW dlmv_pt
PRIMARY KEY(value) BUILD DEFERRED PARTITIONED BY (ds) TBLPROPERTIES
('refresh_mode'='incremental', 'enable_auto_refresh'='true')
AS SELECT value, AVG(value2), ds FROM dlmv_pt_src GROUP BY value, ds;
-- 刷新单分区
ALTER MATERIALIZED VIEW dlmv_pt REBUILD PARTITION(ds='20250730');
刷新动态物化视图的操作详情,请参见手动刷新。
示例4:创建包含参数化定义的动态物化视图
支持参数化定义,可将离线分区作业迁移到增量作业。
-
支持get_setting函数,用于获取Session Flag中设置的参数值。需要以
dps.custom.setting为前缀。 -
传统离线作业中的
${biz_date}替换为get_setting(odps.custom.setting.xx),完成参数化改造。 -
动态物化视图刷新语句前添加Session Flag
set odps.custom.setting.xx=yy。 -
实际运行时,动态物化视图中的
get_setting(odps.custom.setting.xx)会被MaxCompute优化器自动替换为yy。
示例如下:
-- 创建动态物化视图
CREATE MATERIALIZED VIEW mv1
BUILD DEFERRED -- 只做ddl,不生成数据
PARTITIONED BY (ds)
REFRESH EVERY 5 minutes
TBLPROPERTIES("enable_auto_refresh"="true", "refresh_mode"="incremental")
AS
SELECT A.* FROM A JOIN B ON A.c1 = B.c1
AND A.ds=get_setting('odps.custom.setting.bizdate.a')
AND B.ds=get_setting('odps.custom.setting.bizdate.b');
-- 刷新逻辑,通过Dataworks调度,自动替换${biz_date}和${yesterday}
SET odps.custom.setting.bizdate.a=${biz_date};
SET odps.custom.setting.bizdate.b=${yesterday};
ALTER MATERIALIZED VIEW mv1 REBUILD PARTITION(ds=${biz_date});
管理动态物化视图
删除
DROP MATERIALIZED VIEW [IF EXISTS] [<project_name>.]<mv_name>;
手动刷新
动态物化视图提供手动刷新能力,仅支持刷新单分区。其语法和普通物化视图相同:
ALTER MATERIALIZED VIEW [<project_name>.]<mv_name>
REBUILD [PARTITION(<ds>=max_pt(<table_name>),<expression1>...)];
其中ds为分区列。
关闭自动刷新
通过如下命令修改物化视图的TBLPROPERTIES参数,以关闭自动刷新功能。
ALTER MATERIALIZED VIEW <mv_name> SET TBLPROPERTIES("enable_auto_refresh"="false");
恢复自动刷新
通过如下命令修改物化视图的TBLPROPERTIES参数,以打开或者恢复自动刷新。
ALTER MATERIALIZED VIEW <mv_name> SET TBLPROPERTIES("enable_auto_refresh"="true");
修改刷新频率
通过如下命令修改动态物化视图的刷新频率。
ALTER MATERIALIZED VIEW <mv_name>
SET TBLPROPERTIES("refresh_interval_minutes"="xx");
refresh_interval_minutes参数最小取值为1,建议该值小于base table的CDC生命周期。
查看动态物化视图
查看数据变更历史
通过如下命令查看动态物化视图数据的变更记录。
SHOW HISTORY FOR TABLE <mv_name>;
结果示例:
ObjectType ObjectId ObjectName VERSION(LSN) Time Operation
TABLE d95ec7015e8b432e8e0092d01da962a9 incremental_mv 0000000000000001 2024-08-18 21:06:32 CREATE
TABLE d95ec7015e8b432e8e0092d01da962a9 incremental_mv 0000000000000002 2024-08-18 21:11:13 UPDATE
查看刷新记录
通过如下命令查看动态物化视图的刷新记录(仅针对Refresh操作)。
SELECT * FROM
Delta_Live_MV_Refresh_History(['<project_name>', '<schema_name>',]'<table_name>');
参数说明
|
参数 |
描述 |
|
project_name |
项目名称。 |
|
schema_name |
Schema名称。 |
|
table_name |
表名称。 |
返回值说明
|
字段名称 |
描述 |
|
project_name |
Delta Live MV所属的Project。 |
|
schema_name |
Delta Live MV所属的Schema。 |
|
name |
Delta Live MV名称。 |
|
refresh_start_time |
刷新开始时间。 |
|
refresh_end_time |
刷新结束时间。任务状态为RUNNING时,该值为NULL。 |
|
instance_id |
作业ID,通过ID可获取Logview。 |
|
duration_in_seconds |
刷新耗时。 |
|
state |
作业状态。
|
|
refresh_trigger |
刷新方式。
|
|
refresh_mode |
刷新模式。
|
|
error_message |
刷新失败的信息。若刷新成功,则该值为NULL。 |
|
source_tables |
记录了Delta Live MV使用的基表名称以及对应的版本。 |
|
numInsertedRows |
INSERT行数。 |
|
numDeletedRows |
DELETE行数。 |
计费规则
动态物化视图包含计算和存储两部分费用。与普通物化视图操作计算存储计费方式一致。
-
计算费用
-
创建或刷新动态物化视图过程中,涉及到实际启动作业计算的,会消耗计算资源产生计算费用,其计费规则和普通SQL作业一致。
-
触发自动刷新功能时,若无实际增量数据变化,则不会启动SQL作业进行刷新操作,不产生费用。
-
建议将动态物化视图单独放在某个Project中,方便跟踪自动刷新的作业及其计算资源消耗和费用。
-
-
存储费用
-
动态物化视图和普通物化视图或者普通表一样,按照正常存储计量计费。
-
动态物化视图对某些operator可能会采取基于state的增量计算算法,会产生内部state table消耗一定的存储空间。
-
动态物化视图需要增量CDC、Time Travel存储开销,这部分存储开销和普通Delta Table类似。
-