3、物化视图创建语法:CREATE MATERIALIZED VIEW 语法详解、分区与分桶策略、属性配置

好,咱们直接进入正题。物化视图的创建语法,说白了就是 StarRocks 里最核心的那条 DDL。我见过不少同学,建表时很熟练,一到建物化视图就卡壳。其实没那么玄乎,咱们把它拆开揉碎了讲。

3.1 基础语法骨架

先看最基础的写法:

CREATE MATERIALIZED VIEW [IF NOT EXISTS] [database.]mv_name
[COMMENT "comment"]
[PROPERTIES ("key"="value", ...)]
[DISTRIBUTED BY HASH(column_list) [BUCKETS N]]
[REFRESH METHOD]
AS
SELECT ...
FROM ...
[WHERE ...]
[GROUP BY ...];

嗯,这里要注意几个点。方括号里的都是可选,但实际项目中我建议你尽量写全。为什么?因为缺省值不一定适合你的场景。

核心原则:物化视图的本质是「预计算 + 存储」。你写的 SELECT 决定了它存什么,分区和分桶决定了它怎么存,属性决定了它怎么跑。

3.2 分区策略:跟基表走,还是自己来?

分区是物化视图设计里最容易踩坑的地方。我个人习惯遵循一个原则:能跟基表对齐,就尽量对齐。

看个例子:

-- 基表按 dt 分区
CREATE TABLE orders (
    dt DATE,
    order_id INT,
    amount DECIMAL(12,2)
) PARTITION BY RANGE(dt) (
    START ("2024-01-01") END ("2025-01-01") EVERY (INTERVAL 1 MONTH)
);

-- 物化视图也按 dt 分区
CREATE MATERIALIZED VIEW mv_order_daily
PARTITION BY dt
DISTRIBUTED BY HASH(order_id) BUCKETS 16
REFRESH ASYNC
AS
SELECT dt, COUNT(DISTINCT order_id) AS uv, SUM(amount) AS total_amount
FROM orders
GROUP BY dt;

这里 PARTITION BY dt 就是跟基表对齐。好处很明显:基表做分区裁剪时,物化视图也能跟着裁剪。我曾经在一个实时报表项目里,因为没对齐分区,导致每次查询都要扫全量数据,性能直接腰斩。

小技巧:如果物化视图的 GROUP BY 字段和基表分区字段一致,优先用 PARTITION BY 继承基表分区。不一致时,才考虑重新定义分区策略。

那什么时候需要自己定义分区?比如你聚合的粒度是周,但基表按天分区。这时候你可以:

CREATE MATERIALIZED VIEW mv_weekly
PARTITION BY date_trunc('week', dt)
DISTRIBUTED BY HASH(city_id) BUCKETS 12
REFRESH ASYNC
AS
SELECT date_trunc('week', dt) AS week_start,
       city_id,
       SUM(sales) AS weekly_sales
FROM sales_records
GROUP BY week_start, city_id;

注意:物化视图的分区字段必须是 SELECT 列表中的列或表达式。你不能拿一个没出现在查询里的列做分区,StarRocks 会直接报错。

3.3 分桶策略:BUCKETS 设多少才合适?

分桶,说白了就是数据在 BE 节点上的分布方式。我见过有人直接写 BUCKETS 10,也不管集群多大。这其实挺危险的。

分桶数怎么定?我一般按这个公式估算:

建议分桶数 = MAX(集群 BE 节点数 × 2, 数据量预估 / 单桶建议大小)

单桶建议大小在 100MB ~ 1GB 之间。举个例子:

场景 BE 节点数 预估数据量 建议分桶数
小规模 3 10GB 16
中等规模 10 100GB 64
大规模 30 1TB 256

分桶字段的选择也有讲究。我建议选高基数的列,比如 order_id、user_id。别用性别、状态这种低基数字段,否则数据倾斜会让你欲哭无泪。

避坑指南:我曾经在一个用户画像项目里,用 city_id 做分桶,结果北京一个桶占了 40% 的数据,查询时其他节点都在闲着。后来改成 user_id 哈希分桶,才把负载均衡回来。

3.4 属性配置:这些参数能救命

属性配置是物化视图的「调优开关」。我挑几个最常用的说说:

属性名 作用 我的建议
replication_num 副本数,默认 3 测试环境设 1,生产环境保持默认
storage_medium 存储介质,SSD 或 HDD 高频查询用 SSD,历史归档用 HDD
refresh_interval 异步刷新间隔,单位秒 实时性要求高设 60,一般场景 300
partition_ttl_number 保留最近多少个分区 按业务需求设,避免数据无限膨胀
auto_refresh_partitions_limit 每次刷新最多处理多少分区 默认 10,大分区场景可以调大

看个完整配置的例子:

CREATE MATERIALIZED VIEW mv_user_behavior
PARTITION BY dt
DISTRIBUTED BY HASH(user_id) BUCKETS 48
REFRESH ASYNC
PROPERTIES (
    "replication_num" = "3",
    "storage_medium" = "SSD",
    "refresh_interval" = "120",
    "partition_ttl_number" = "90",
    "auto_refresh_partitions_limit" = "20"
)
AS
SELECT dt,
       user_id,
       COUNT(*) AS pv,
       COUNT(DISTINCT session_id) AS session_cnt
FROM user_logs
GROUP BY dt, user_id;

经验之谈:partition_ttl_number 这个属性我特别喜欢。它自动帮你清理过期分区,省得写定时任务。比如设成 90,就只保留最近 90 个分区,再老的数据自动删除。

3.5 整体流程一张图

说了这么多,咱们用一张图把整个创建流程串起来:

物化视图创建流程 1. 定义查询逻辑 2. 选择分区策略 3. 设定分桶策略 4. 配置属性参数 5. 执行创建 DDL 💡 每一步的决策都影响最终性能,建议先在测试环境验证 分区对齐基表 → 分桶选高基数列 → 属性按场景调优

3.6 几个容易忽略的细节

  • IF NOT EXISTS 一定要加:特别是在自动化脚本里,不加的话重复执行会报错,打断整个流程。
  • COMMENT 别偷懒:写上物化视图的用途、刷新频率、依赖的表。三个月后你回来维护时,会感谢现在的自己。
  • SELECT 里别用 *:显式列出需要的列,这样基表加字段时物化视图不会受影响。
  • GROUP BY 字段顺序:把高基数字段放前面,低基数字段放后面,能提升聚合效率。

重要提醒:物化视图创建后,如果基表结构发生变化(比如加列、删列),物化视图不会自动同步。你需要手动 ALTER MATERIALIZED VIEW ... REFRESH 或者重建。所以生产环境里,我建议把物化视图的 DDL 也纳入版本管理。

好了,创建语法这块就讲到这里。语法本身不复杂,但每个参数背后都有 trade-off。多想想你的业务场景,多测试几组配置,慢慢就能找到手感。