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 整体流程一张图
说了这么多,咱们用一张图把整个创建流程串起来:
3.6 几个容易忽略的细节
- IF NOT EXISTS 一定要加:特别是在自动化脚本里,不加的话重复执行会报错,打断整个流程。
- COMMENT 别偷懒:写上物化视图的用途、刷新频率、依赖的表。三个月后你回来维护时,会感谢现在的自己。
- SELECT 里别用 *:显式列出需要的列,这样基表加字段时物化视图不会受影响。
- GROUP BY 字段顺序:把高基数字段放前面,低基数字段放后面,能提升聚合效率。
重要提醒:物化视图创建后,如果基表结构发生变化(比如加列、删列),物化视图不会自动同步。你需要手动 ALTER MATERIALIZED VIEW ... REFRESH 或者重建。所以生产环境里,我建议把物化视图的 DDL 也纳入版本管理。
好了,创建语法这块就讲到这里。语法本身不复杂,但每个参数背后都有 trade-off。多想想你的业务场景,多测试几组配置,慢慢就能找到手感。