分区物化视图

更新时间:
复制 MD 格式

分区物化视图是PolarDB PostgreSQL提供的高级物化视图特性,能够基于分区表自动创建对应的分区物化视图,实现分区级别的增量刷新,大幅提升刷新效率。

概述

分区物化视图支持两种分区对齐模式:

  • 等比对齐(Proportional):源表分区与物化视图分区一一对应。每个源分区对应一个子物化视图,刷新时仅刷新数据发生变化的分区。适用于源表分区粒度与物化视图查询粒度一致的场景。

  • 上卷对齐(Rollup):物化视图按时间维度进行上卷聚合,将多个源分区的数据合并到一个物化视图分区中。例如源表按日分区,物化视图按月聚合。系统会根据查询定义中的IMMUTABLE聚合表达式(如LEFT(date_str, 6))自动识别上卷关系。

两种模式使用相同的接口,系统根据查询定义自动判断对齐类型。

适用范围

扩展

需先执行以下SQL安装polar_ivm扩展:

CREATE EXTENSION IF NOT EXISTS polar_ivm;

分区类型要求

分区类型

支持状态

RANGE 分区

支持

LIST 分区

不支持

HASH 分区

不支持

多列分区键

不支持(仅支持单列)

查询定义要求

  • 分区键必须在 GROUP BY 中:聚合查询时,分区键(或其上卷表达式)必须出现在GROUP BY子句中。

  • 分区表不能在子查询中:分区表不能出现在WHERE子查询、FROM子查询或CTE中。

  • 分区表不能在 JOIN 可空侧LEFT JOIN时分区表必须在左侧,RIGHT JOIN时分区表必须在右侧。

  • 不能多次引用分区表:查询中不能多次引用同一个分区表。

  • 不能引用其他物化视图:查询中不能引用其他已有的物化视图。

  • 不能包含易变函数:不能使用CURRENT_DATErandom()等非不可变函数。

增量刷新机制

分区物化视图通过版本追踪实现智能增量刷新:

场景

刷新行为

分区数据未变化

跳过该分区(除非force_refresh=TRUE)。

分区数据发生变化

仅刷新该分区。

JOIN 的其他表数据变化

刷新所有分区。

源表新增/删除分区

刷新时自动同步结构并刷新新分区。

接口说明

分区物化视图通过polar_matview schema 提供三个存储过程管理其生命周期:create_partition_mv创建、refresh_partition_mv刷新、drop_partition_mv删除。三个存储过程均需以CALL方式调用。

创建分区物化视图

使用polar_matview.create_partition_mv存储过程创建分区物化视图:

CALL polar_matview.create_partition_mv(
  mv_schema        => 'public',
  mv_name          => 'my_mv',
  ref_partition_table => 'public.orders'::regclass::oid,
  view_def         => 'SELECT order_date, COUNT(*) AS cnt FROM public.orders GROUP BY order_date',
  use_imci         => FALSE
);

参数说明

参数

类型

默认值

说明

mv_schema

NAME

必填

物化视图所在的schema。

mv_name

NAME

必填

物化视图名称。

ref_partition_table

OID

必填

源分区表的OID,可通过'schema.table'::regclass获取。

view_def

TEXT

必填

物化视图的SELECT查询语句。

use_imci

BOOLEAN

FALSE

是否使用列存索引(IMCI)加速创建和刷新。

对齐模式判断:系统根据view_def中的查询定义自动判断对齐模式。如果GROUP BY中的分区键表达式与源表分区键完全一致,则为等比对齐。如果GROUP BY中使用了IMMUTABLE聚合表达式(如LEFT(col, 6)),则自动识别为上卷对齐。

说明

“上卷对齐”会使用GROUP BY中第一个带有分区键的表达式作为物化视图的分区键,请调整视图定义,将需要的分区表达式放在前面。

刷新分区物化视图

使用polar_matview.refresh_partition_mv存储过程刷新分区物化视图:

CALL polar_matview.refresh_partition_mv(
  mv_schema     => 'public',
  mv_name       => 'my_mv',
  use_imci      => FALSE,
  force_refresh => FALSE
);

参数说明

参数

类型

默认值

说明

mv_schema

NAME

必填

物化视图所在的schema。

mv_name

NAME

必填

物化视图名称。

use_imci

BOOLEAN

FALSE

是否使用列存索引(IMCI)加速刷新。

force_refresh

BOOLEAN

FALSE

设为TRUE时强制刷新所有分区,忽略版本检查。

刷新行为

  • 默认情况下,刷新时会自动同步源表的分区结构变化(新增/删除分区),然后仅刷新数据版本发生变化的分区。

  • 设置force_refresh=TRUE时,忽略版本检查,强制刷新所有分区。

删除分区物化视图

使用polar_matview.drop_partition_mv存储过程删除分区物化视图及其所有子分区物化视图,同时清理相关元数据:

CALL polar_matview.drop_partition_mv(
  mv_schema => 'public',
  mv_name   => 'my_mv'
);

参数说明

参数

类型

默认值

说明

mv_schema

NAME

必填

物化视图所在的schema。

mv_name

NAME

必填

物化视图名称。

使用示例

等比对齐示例

源表按日分区,物化视图也按日分区,源分区与物化视图分区一一对应。

-- 创建源分区表 (RANGE 按 order_date 单列分区)
CREATE TABLE orders (
  order_id   BIGSERIAL,
  order_date DATE NOT NULL,
  amount     NUMERIC(12, 2)
) PARTITION BY RANGE (order_date);

CREATE TABLE orders_20260101 PARTITION OF orders
  FOR VALUES FROM ('2026-01-01') TO ('2026-01-02');
CREATE TABLE orders_20260102 PARTITION OF orders
  FOR VALUES FROM ('2026-01-02') TO ('2026-01-03');

-- 创建等比对齐的分区物化视图:GROUP BY 使用分区键 order_date 本身
CALL polar_matview.create_partition_mv(
  mv_schema        => 'public',
  mv_name          => 'orders_daily_mv',
  ref_partition_table => 'public.orders'::regclass::oid,
  view_def         => 'SELECT order_date, COUNT(*) AS cnt, SUM(amount) AS total FROM public.orders GROUP BY order_date',
  use_imci         => FALSE
);

-- 刷新分区物化视图(仅刷新数据发生变化的分区)
CALL polar_matview.refresh_partition_mv(
  mv_schema => 'public',
  mv_name   => 'orders_daily_mv'
);

上卷对齐示例

源表按细粒度分区,物化视图按粗粒度聚合,多个源分区合并到一个物化视图分区。

示例:TEXT 类型分区键 + LEFT() 上卷(按日分区源表 → 按月聚合物化视图)

-- ========== 1. 创建按日分区的源表(分区键为 TEXT 类型 'YYYYMMDD') ==========
DROP SCHEMA IF EXISTS report CASCADE;
CREATE SCHEMA report;

CREATE TABLE report.daily_data (
  log_date TEXT NOT NULL,   -- 格式:'YYYYMMDD',如 '20240105'
  value    INTEGER
) PARTITION BY RANGE (log_date);

-- 每个源分区对应一天
CREATE TABLE report.daily_data_20240105 PARTITION OF report.daily_data
  FOR VALUES FROM ('20240105') TO ('20240106');
CREATE TABLE report.daily_data_20240115 PARTITION OF report.daily_data
  FOR VALUES FROM ('20240115') TO ('20240116');
CREATE TABLE report.daily_data_20240125 PARTITION OF report.daily_data
  FOR VALUES FROM ('20240125') TO ('20240126');
CREATE TABLE report.daily_data_20240205 PARTITION OF report.daily_data
  FOR VALUES FROM ('20240205') TO ('20240206');
CREATE TABLE report.daily_data_20240215 PARTITION OF report.daily_data
  FOR VALUES FROM ('20240215') TO ('20240216');
CREATE TABLE report.daily_data_20240305 PARTITION OF report.daily_data
  FOR VALUES FROM ('20240305') TO ('20240306');

INSERT INTO report.daily_data VALUES
  ('20240105', 20), ('20240115', 20), ('20240125', 20),  -- 2024-01 合计 60
  ('20240205', 40), ('20240215', 50),                     -- 2024-02 合计 90
  ('20240305', 60);                                        -- 2024-03 合计 60

-- ========== 2. 创建按月聚合的上卷物化视图 ==========
-- GROUP BY 中使用 LEFT(log_date, 6) 对分区键上卷,系统自动识别为上卷对齐
-- 6 个按日的源分区会合并到 3 个按月的物化视图分区
CALL polar_matview.create_partition_mv(
  mv_schema        => 'report',
  mv_name          => 'mv_monthly_summary',
  ref_partition_table => 'report.daily_data'::regclass::oid,
  view_def         => 'SELECT LEFT(log_date, 6) AS month, SUM(value) AS total FROM report.daily_data GROUP BY LEFT(log_date, 6)',
  create_no_data   => TRUE
);

CALL polar_matview.refresh_partition_mv(
  mv_schema => 'report',
  mv_name   => 'mv_monthly_summary'
);

-- ========== 3. 查询结果 ==========
SELECT * FROM report.mv_monthly_summary ORDER BY month;
--  month  | total
-- --------+-------
--  202401 |    60   -- 合并 20240105/20240115/20240125 三个日分区
--  202402 |    90   -- 合并 20240205/20240215 两个日分区
--  202403 |    60   -- 对应 20240305 一个日分区

-- ========== 4. 增量刷新(仅刷新数据发生变化的分区) ==========
INSERT INTO report.daily_data VALUES ('20240215', 10);
CALL polar_matview.refresh_partition_mv(
  mv_schema => 'report',
  mv_name   => 'mv_monthly_summary'
);
-- 版本追踪自动识别:仅 20240215 所在的 2024-02 上卷分区被刷新,
-- 2024-01 和 2024-03 分区未变化被跳过

-- ========== 5. 新增日分区后再次刷新 ==========
CREATE TABLE report.daily_data_20240315 PARTITION OF report.daily_data
  FOR VALUES FROM ('20240315') TO ('20240316');
INSERT INTO report.daily_data VALUES ('20240315', 30);

CALL polar_matview.refresh_partition_mv(
  mv_schema => 'report',
  mv_name   => 'mv_monthly_summary'
);
-- 系统自动同步源表新增的分区结构,并把 20240315 数据上卷到 2024-03 分区
SELECT * FROM report.mv_monthly_summary ORDER BY month;
--  month  | total
-- --------+-------
--  202401 |    60
--  202402 |   100   -- 因追加 20240215 的 10 变为 100
--  202403 |    90   -- 因新增 20240315 的 30 变为 90
说明

上卷表达式必须是IMMUTABLE函数。PostgreSQL内置的date_trunc()to_char()STABLE函数(依赖会话参数),不能直接用作分区键。若源表分区键是DATE类型且需要按月上卷,建议将分区键改为TEXT类型(如'YYYYMMDD'格式)后使用LEFT()上卷,或自行封装一个IMMUTABLE的日期截断函数。