4. 物化视图刷新策略:异步刷新、手动刷新、定时刷新、增量刷新与全量刷新的选择

刷新策略选不对,物化视图就是个摆设。我见过太多人建完视图就不管了,结果数据对不上,查询还变慢。说白了,刷新策略就是决定「什么时候用多少代价把数据同步过来」。选错了,要么资源打满,要么数据永远滞后。

StarRocks 给了我们好几种刷新方式。怎么选?我习惯先问自己三个问题:

  • 数据能容忍多久的延迟?
  • 每次刷新的数据量有多大?
  • 集群资源够不够折腾一次全量?

想清楚这些,策略自然就出来了。

核心原则:能增量就别全量,能异步就别同步,能定时就别手动。但凡事有例外,后面我会讲哪些场景必须用全量。

4.1 异步刷新:最常用的方式

异步刷新,说白了就是「你告诉 StarRocks 去刷新,它答应一声就去后台干了,不堵你当前操作」。这是我最推荐的方式,尤其是数据量大的时候。

为什么?因为同步刷新会把查询和刷新绑在一起。你想想看,一个跑 10 分钟的任务堵在前面,后面的查询全得等着,这谁受得了?

异步刷新的用法很简单:

-- 创建一个物化视图,默认就是异步刷新
CREATE MATERIALIZED VIEW mv_order_summary
DISTRIBUTED BY HASH(order_date)
REFRESH ASYNC
AS
SELECT order_date, COUNT(*) AS order_count, SUM(amount) AS total_amount
FROM orders
GROUP BY order_date;

-- 手动触发异步刷新
REFRESH MATERIALIZED VIEW mv_order_summary;

我在项目中遇到过一个问题:有个团队把所有物化视图都配成了同步刷新,结果业务高峰期查询超时一片。后来改成异步,世界清净了。嗯,这里要注意,异步刷新不代表没延迟,你得接受数据有几秒甚至几分钟的滞后。

4.2 手动刷新:适合你说了算的场景

手动刷新,就是完全由你控制刷新时机。StarRocks 不会自动碰它,你什么时候调用 REFRESH 命令,它什么时候干活。

什么场景用?我总结了几种:

  • ETL 完成后:数据 pipeline 跑完了,你手动刷一下,保证物化视图和源表一致。
  • 大促/报表前:我知道第二天要出报表,提前一晚手动刷新,避免白天抢资源。
  • 调试阶段:建完视图先手动刷一次,看看数据对不对,别上来就开定时任务。

我的习惯:开发环境全部用手动刷新。等逻辑验证完了,再上生产环境配定时或异步。这样能避免「建完视图发现逻辑错了,还得先清数据」的尴尬。

4.3 定时刷新:让 StarRocks 帮你盯着

定时刷新,就是给物化视图设个闹钟。StarRocks 到点就自动跑一次刷新任务。语法上支持两种方式:START 指定首次执行时间,EVERY 指定间隔。

-- 每天凌晨 2 点刷新一次
CREATE MATERIALIZED VIEW mv_daily_sales
REFRESH ASYNC
START('2025-01-01 02:00:00')
EVERY(INTERVAL 1 DAY)
AS
SELECT sale_date, SUM(revenue) AS total_revenue
FROM sales
GROUP BY sale_date;

我个人习惯把定时刷新用在「数据变更有规律」的场景。比如:

  • 每天凌晨 ETL 跑完,2 点刷新物化视图,早上 9 点业务查询时数据已经准备好了。
  • 每小时刷新一次,用于近实时报表,延迟控制在 5 分钟内。

曾经踩过的坑:我把定时刷新间隔设成了 5 分钟,但每次刷新要跑 8 分钟。结果任务一个叠一个,集群 CPU 直接飙到 95%。后来我学乖了,刷新间隔必须大于单次执行时间,最好留 50% 的余量。

4.4 增量刷新 vs 全量刷新:核心选择

这是最关键的决策。选错了,性能天差地别。

对比维度 增量刷新 全量刷新
刷新数据量 只处理新增/变更的数据 重新计算全部数据
资源消耗 低,适合频繁刷新 高,适合低频刷新
适用场景 大表、高频变更、实时性要求高 小表、首次构建、数据重算
实现复杂度 需要基表支持分区或主键模型 无特殊要求

增量刷新怎么用?看个例子:

-- 基表使用分区表,物化视图按分区增量刷新
CREATE TABLE orders (
    order_id BIGINT,
    order_date DATE,
    amount DECIMAL(10,2)
) PARTITION BY RANGE(order_date) (
    START('2024-01-01') END('2025-12-31') EVERY(INTERVAL 1 DAY)
);

CREATE MATERIALIZED VIEW mv_order_daily
REFRESH ASYNC
AS
SELECT order_date, COUNT(*) AS cnt, SUM(amount) AS total
FROM orders
GROUP BY order_date;

-- 增量刷新:只刷新有数据变更的分区
REFRESH MATERIALIZED VIEW mv_order_daily
PARTITION START('2025-01-15') END('2025-01-16');

我曾经接手过一个项目,物化视图每天全量刷新,跑一次要 40 分钟。后来改成增量刷新,每次只处理当天新增的几百万条数据,耗时降到 3 分钟。你想想看,这差距有多大。

但增量刷新不是万能的。有几种情况你必须用全量:

  • 首次构建:物化视图刚创建,没有任何历史数据,必须全量刷一次。
  • 数据回溯:源表历史数据被修正了,增量刷新不会处理已刷过的分区,你得全量重算。
  • 聚合逻辑复杂:比如用了 DISTINCT 或嵌套聚合,StarRocks 可能无法准确识别增量范围。

我的建议:能用增量就用增量。如果必须全量,尽量安排在业务低峰期,并且给刷新任务设置资源组限制,别让它把集群吃光。

4.5 刷新策略选择流程图

下面这张图是我自己总结的决策流程。每次选策略时,我都会过一遍:

物化视图刷新策略选择流程 开始选择刷新策略 数据量 > 1000 万行? 是 数据是增量追加还是全量覆盖? 增量追加 增量刷新 + 异步 全量覆盖 全量刷新 + 定时 否 全量刷新 + 手动 实时性要求 < 5 分钟? 是 定时刷新(EVERY 5 MIN) 否 定时刷新(EVERY 1 DAY) 说明:首次构建或数据回溯时,无论选哪种策略,都必须先执行一次全量刷新

4.6 避坑指南与最佳实践

最后,分享几个我这些年攒下来的经验:

  • 不要所有视图都用同一种策略。大表用增量,小表用全量,实时报表用定时,调试用手动。一刀切最容易出问题。
  • 监控刷新任务的状态。StarRocks 提供了 information_schema 视图,你可以查 tasks 表看刷新进度。我曾经发现一个视图连续失败 3 天没人管,数据落后了 72 小时。
  • 全量刷新前评估资源。如果一次全量要扫描几百 GB 数据,建议先调大 query_mem_limit,或者用资源组隔离,别让它影响线上查询。
  • 增量刷新依赖基表结构。基表必须是分区表或主键模型,否则 StarRocks 无法识别增量范围。建表时就要规划好,别等建完物化视图才发现不支持。

曾经踩过的坑:有一次我把物化视图的刷新间隔设成了 1 分钟,但基表每 5 分钟才写入一批数据。结果 StarRocks 每 1 分钟跑一次刷新,每次都扫不到新数据,白白浪费资源。后来我把间隔改成 5 分钟,和写入节奏对齐,问题就解决了。

刷新策略没有银弹。你得根据数据量、变更频率、实时性要求和集群资源来综合判断。我的建议是:先用增量 + 异步兜底,遇到性能瓶颈再针对性调整。别一上来就搞全量定时,那是给自己找麻烦。