订阅更新

报表查询太慢,加了物化视图快了20倍——Rollup和物化视图怎么用?

Doris手册 第 6 题 约 10 分钟读完

本文目录
  1. 先说结论
  2. Rollup——Doris的经典预聚合
  3. 物化视图——更强大的预计算(推荐)
  4. Rollup vs 物化视图对比
  5. 什么时候该加物化视图?——我的判断标准
  6. 实战经验:从5秒到300ms的优化过程
  7. 实战踩坑:物化视图命中失败
  8. 面试高频追问
  9. 总结

先说结论

Rollup和物化视图本质上都是'预计算':提前把常用的聚合查询结果算好存起来,查询时直接读预计算结果,不用每次都扫原始数据。Rollup是Doris早期的预聚合机制,物化视图是Doris 2.0+推荐的更强大的替代方案。

核心区别:Rollup只能做列的子集+预聚合,物化视图可以包含表达式计算、WHERE过滤、JOIN等更复杂的逻辑。新项目建议直接用物化视图。

先把这段讲顺;面试官追问时,再展开下面的原理和细节。

详细讲解

在某元宇宙社交公司做实时大盘时,有一张用户行为明细表5000万条/天,运营经常查的是每日UV和GMV汇总。每次查都要扫全量明细做GROUP BY,响应时间5-8秒,运营刷大盘的时候体验很差。后来加了物化视图,同样的查询降到300ms以内,快了20倍。物化视图和Rollup是Doris查询加速的核心手段,面试必考。

Rollup——Doris的经典预聚合

Rollup本质是原表的一个'副本',只包含部分列,数据按Rollup的Key自动预聚合。查询时Doris自动选择最优的Rollup命中。

-- 原表:用户行为明细(Duplicate模型)
CREATE TABLE ods_user_event (
    event_time DATETIME,
    user_id BIGINT,
    event_name VARCHAR(128),
    page_id VARCHAR(64),
    device_id VARCHAR(64),
    country VARCHAR(32),
    amount DECIMAL(12,2)
) DUPLICATE KEY(event_time, user_id)
PARTITION BY RANGE(event_time) (...)
DISTRIBUTED BY HASH(user_id) BUCKETS 16;
 
-- 创建Rollup:按天+国家的UV/PV/GMV预聚合
ALTER TABLE ods_user_event ADD ROLLUP rollup_daily_country (
    event_time, country, user_id, amount
);
-- Doris会自动维护这个Rollup
-- 查询 SELECT country, COUNT(DISTINCT user_id), SUM(amount)
-- FROM ods_user_event WHERE event_time = 'xxx' GROUP BY country
-- 会自动命中这个Rollup,速度提升10-20倍

Rollup的局限:① 只能是原表列的子集,不能加表达式计算;② 只能做简单的聚合(SUM/MIN/MAX/COUNT),不能做复杂的窗口函数;③ 每个Rollup都会占用额外存储空间,加太多会增加导入耗时。

物化视图——更强大的预计算(推荐)

物化视图(Materialized View)是Doris 2.0+引入的更强大的预计算机制,可以包含表达式、WHERE条件,甚至跨表JOIN(异步物化视图):

-- 同步物化视图(单表,自动维护,查询自动命中)
CREATE MATERIALIZED VIEW mv_daily_stats AS
SELECT
    DATE(event_time) AS dt,
    country,
    COUNT(DISTINCT user_id) AS uv,
    COUNT(*) AS pv,
    SUM(amount) AS total_amount,
    AVG(amount) AS avg_amount
FROM ods_user_event
GROUP BY DATE(event_time), country;
 
-- 查询时不需要指定物化视图名称
-- Doris自动判断是否能命中物化视图
SELECT dt, country, uv, pv, total_amount
FROM ods_user_event  -- 还是查原表!
WHERE dt = '2024-01-15'
GROUP BY dt, country;
-- Doris自动路由到mv_daily_stats,秒出结果
-- 异步物化视图(Doris 2.1+,支持多表JOIN,更灵活)
CREATE MATERIALIZED VIEW mv_user_gift_stats
BUILD IMMEDIATE       -- 创建后立即构建
REFRESH AUTO           -- 自动刷新
AS
SELECT
    DATE(e.event_time) AS dt,
    u.country,
    u.vip_level,
    COUNT(DISTINCT e.user_id) AS uv,
    SUM(e.amount) AS total_amount
FROM ods_user_event e
JOIN dim_user u ON e.user_id = u.user_id
WHERE e.event_name = 'gift_send'
GROUP BY DATE(e.event_time), u.country, u.vip_level;
 
-- 这个物化视图包含了JOIN + WHERE过滤 + 聚合
-- 查询 '各国各VIP等级的送礼金额' 自动命中,秒出

Rollup vs 物化视图对比

维度Rollup同步物化视图异步物化视图
定义方式ALTER TABLE ADD ROLLUPCREATE MATERIALIZED VIEWCREATE MV ... BUILD/REFRESH
支持表达式❌ 只能选原表列✅ 支持DATE()/SUM()等✅ 支持复杂表达式
支持JOIN❌❌✅ 支持多表JOIN
支持WHERE❌❌✅ 支持过滤条件
维护方式导入时自动同步导入时自动同步定时/手动刷新
查询路由自动命中自动命中自动命中(2.1+)
推荐度旧版本用单表聚合推荐复杂场景推荐

什么时候该加物化视图?——我的判断标准

不要无脑加物化视图,它有存储和写入开销。我在元宇宙社交公司的判断标准:

① 查询频率高:同一种聚合查询每天被执行100次以上(比如实时大盘每分钟刷新一次),值得加物化视图。偶尔跑一次的临时分析不值得。

② 原始数据量大:原表有几千万到几亿行,每次GROUP BY都要全表扫描很慢。如果原表只有几十万行,加不加物化视图差别不大。

③ 聚合维度固定:大盘查的维度基本固定(按天、按国家、按事件类型),不会天天变。如果维度经常变化,物化视图就要频繁重建,得不偿失。

实战经验:从5秒到300ms的优化过程

在某元宇宙社交公司,实时大盘有一个核心查询:查每天每个国家的UV、PV、GMV。原始数据在ods_user_event表,每天5000万条。

优化前:直接查原表做GROUP BY,扫描5000万行,响应时间5-8秒。大盘每分钟自动刷新,每次都要等好几秒,运营同事体验很差。

优化方案:创建同步物化视图mv_daily_stats,包含DATE(event_time)、country、COUNT(DISTINCT user_id)、COUNT(*)、SUM(amount)。

优化后:同样的查询Doris自动路由到物化视图,只需要扫描几百行预聚合数据,响应时间降到200-300ms。大盘刷新体验变得流畅。

额外开销:物化视图大约占用原表5%的存储空间,数据导入延迟增加了约10%(因为要同步更新物化视图)。对于我们的场景完全可以接受。

实战踩坑:物化视图命中失败

有一次我创建了一个物化视图用来加速'按天统计UV'的查询,但发现查询速度没有任何提升。用EXPLAIN看执行计划,发现没有命中物化视图。

-- 物化视图定义
CREATE MATERIALIZED VIEW mv_daily_uv AS
SELECT DATE(event_time) AS dt, COUNT(DISTINCT user_id) AS uv
FROM ods_user_event GROUP BY DATE(event_time);
 
-- 查询(没有命中!)
SELECT DATE(event_time) AS dt, COUNT(DISTINCT user_id) AS uv
FROM ods_user_event
WHERE event_name = 'page_view'  -- 多了一个过滤条件!
GROUP BY DATE(event_time);
 
-- 原因:物化视图没有event_name列,无法做过滤
-- 查询带了WHERE event_name = 'page_view',物化视图无法满足
 
-- 修复:在物化视图里加上event_name维度
CREATE MATERIALIZED VIEW mv_daily_event_uv AS
SELECT DATE(event_time) AS dt, event_name,
       COUNT(DISTINCT user_id) AS uv
FROM ods_user_event GROUP BY DATE(event_time), event_name;

教训:物化视图能否命中取决于查询SQL能不能从物化视图的预计算结果中得到答案。如果查询带了物化视图没有的列做过滤或分组,就命中不了。创建物化视图前要分析最常用的查询模式,把常用的WHERE和GROUP BY列都包含进去。

面试高频追问

追问一:物化视图会影响写入性能吗?

会。同步物化视图在数据导入时需要同步更新,物化视图越多、越复杂,导入延迟就越高。我的经验是:一张表的同步物化视图不要超过5个,否则导入延迟会明显增加。如果需要更多预计算,考虑用异步物化视图+定时刷新。

追问二:怎么知道查询有没有命中物化视图?

用EXPLAIN命令查看执行计划。如果命中了物化视图,计划里会显示物化视图的名字(比如mv_daily_stats)。如果没命中,显示的是原表名。这是排查物化视图问题的第一步。

追问三:物化视图和在DWS层建汇总表有什么区别?

本质上是一回事——都是预计算。区别在于:物化视图由Doris自动维护(数据导入时同步更新),DWS汇总表需要你写调度任务手动更新。物化视图更省心但灵活性有限,DWS汇总表更灵活但要维护调度逻辑。我的做法是简单的预聚合用物化视图,复杂的ETL逻辑建DWS表。

总结

物化视图面试讲清楚三点:① 原理(预计算,查询自动路由);② Rollup vs 同步物化视图 vs 异步物化视图的区别和适用场景;③ 什么时候该加(查询频繁+数据量大+维度固定)。踩坑点是物化视图命中条件——查询的WHERE和GROUP BY列必须被物化视图覆盖。能讲出从5秒优化到300ms的实际案例和量化数字,面试官会很满意。

下一篇聊Doris的JOIN优化:两张大表JOIN很慢怎么办?Colocate Join、Bucket Shuffle Join都是什么?

你们Doris用了物化视图吗?效果如何?评论区聊聊~

关注「Wood聊大数据」| 9年大数据老兵,聊技术、聊职场、聊成长

刷题进度保存在当前浏览器