物化视图查询改写
本文描述了如何利用 StarRocks 的异步物化视图来改写并加速查询。
概述
StarRocks 的异步物化视图采用了主流的基于 SPJG(select-project-join-group-by)模式透明查询改写算法。在不修改查询语句的前提下,StarRocks 可以自动将在基表上的查询改写为在物化视图上的查询。通过其中包含的预计算结果,物化视图可以帮助您显著降低计算成本,并大幅加速查询执行。
基于异步物化视图的查询改写功能,在以下场景下特别有用:
-
指标预聚合
如果您需要处理高维度数据,可以使用物化视图来创建预聚合指标层。
-
宽表 Join
物化视图允许您在复杂场景下下透明加速包含大宽表 Join 的查询。
-
湖仓加速
构建基于 External Catalog 的物化视图可以轻松加速针对数据湖中数据的查询。
说明
基于 JDBC Catalog 表构建的异步物化视图暂不支持查询改写。
功能特点
StarRocks 的异步物化视图自动查询改写功能具有以下特点:
- 强数据一致性:如果基表是 StarRocks 内表,StarRocks 可以保证通过物化视图查询改写获得的结果与直接查询基表的结果一致。
- Staleness rewrite:StarRocks 支持 Staleness rewrite,即允许容忍一定程度的数据过期,以应对数据变更频繁的情况。
- 多表 Join:StarRocks 的异步物化视图支持各种类型的 Join,包括一些复杂的 Join 场景,如 View Delta Join 和 Join 派生改写,可用于加速涉及大宽表的查询场景。
- 聚合改写:StarRocks 可以改写带有聚合操作的查询,以提高报表性能。
- 嵌套物化视图:StarRocks 支持基于嵌套物化视图改写复杂查询,扩展了可改写的查询范围。
- Union 改写:您可以将 Union 改写特性与物化视图分区的生存时间(TTL)相结合,实现冷热数据的分离,允许您从物化视图查询热数据,从基表查询历史数据。
- 基于视图构建物化视图:您可以在基于视图建模的情景下加速查询。
- 基于 External Catalog 构建物化视图:您可以通过该特性加速数据湖中的查询。
- 复杂表达式改写:支持在表达式中调用函数和算术运算,满足复杂分析和计算需求。
这些特点将在以下各节中详细说明。
Join 改写
StarRocks 支持改写具有各种类型 Join 的查询,包括 Inner Join、Cross Join、Left Outer Join、Full Outer Join、Right Outer Join、Semi Join 和 Anti Join。 以下示例展示 Join 查询的改写。创建以下基表:
CREATE TABLE customer (
c_custkey INT(11) NOT NULL,
c_name VARCHAR(26) NOT NULL,
c_address VARCHAR(41) NOT NULL,
c_city VARCHAR(11) NOT NULL,
c_nation VARCHAR(16) NOT NULL,
c_region VARCHAR(13) NOT NULL,
c_phone VARCHAR(16) NOT NULL,
c_mktsegment VARCHAR(11) NOT NULL
) ENGINE=OLAP
DUPLICATE KEY(c_custkey)
DISTRIBUTED BY HASH(c_custkey) BUCKETS 12;
CREATE TABLE lineorder (
lo_orderkey INT(11) NOT NULL,
lo_linenumber INT(11) NOT NULL,
lo_custkey INT(11) NOT NULL,
lo_partkey INT(11) NOT NULL,
lo_suppkey INT(11) NOT NULL,
lo_orderdate INT(11) NOT NULL,
lo_orderpriority VARCHAR(16) NOT NULL,
lo_shippriority INT(11) NOT NULL,
lo_quantity INT(11) NOT NULL,
lo_extendedprice INT(11) NOT NULL,
lo_ordtotalprice INT(11) NOT NULL,
lo_discount INT(11) NOT NULL,
lo_revenue INT(11) NOT NULL,
lo_supplycost INT(11) NOT NULL,
lo_tax INT(11) NOT NULL,
lo_commitdate INT(11) NOT NULL,
lo_shipmode VARCHAR(11) NOT NULL
) ENGINE=OLAP
DUPLICATE KEY(lo_orderkey)
DISTRIBUTED BY HASH(lo_orderkey) BUCKETS 48;
基于上述基表,创建以下物化视图:
CREATE MATERIALIZED VIEW join_mv1
DISTRIBUTED BY HASH(lo_orderkey)
AS
SELECT lo_orderkey, lo_linenumber, lo_revenue, lo_partkey, c_name, c_address
FROM lineorder INNER JOIN customer
ON lo_custkey = c_custkey;
该物化视图可以改写以下查 询:
SELECT lo_orderkey, lo_linenumber, lo_revenue, c_name, c_address
FROM lineorder INNER JOIN customer
ON lo_custkey = c_custkey;
其原始查询计划和改写后的计划如下:
StarRocks 支持改写具有复杂表达式的 Join 查询,如算术运算、字符串函数、日期函数、CASE WHEN 表达式和谓词 OR 等。例如,上述物化视图可以改写以下查询:
SELECT
lo_orderkey,
lo_linenumber,
(2 * lo_revenue + 1) * lo_linenumber,
upper(c_name),
substr(c_address, 3)
FROM lineorder INNER JOIN customer
ON lo_custkey = c_custkey;
除了常规场景,StarRocks还支持在更复杂的情景下改写 Join 查询。
Query Delta Join 改写
Query Delta Join 是指查询中 Join 的表是物化视图中 Join 的表的超集的情况。例如,以下查询 Join 了表 lineorder
、表 customer
和 表 part
。如果物化视图 join_mv1
仅包含 lineorder
和 customer
的 Join,StarRocks 可以使用 join_mv1
来改写查询。
示例:
SELECT lo_orderkey, lo_linenumber, lo_revenue, c_name, c_address, p_name
FROM
lineorder INNER JOIN customer ON lo_custkey = c_custkey
INNER JOIN part ON lo_partkey = p_partkey;
其原始查询计划和改写后的计划如下:
View Delta Join 改写
View Delta Join 指的是查询中 Join 的表是物化视图中 Join 的表的子集的情况。通常在涉及大宽表的情景中使用此功能。例如,在 Star Schema Benchmark (SSB) 的背景下,您可以通过创建物化视图,Join 所有表以提高查询性能。测试发现在通过物化视图透明改写查询后,多表 Join 的查询性能可以达到与查询相应大宽表相同的性能水平。
要启用 View Delta Join 改写,必须保证物化视图中包含在查询中不存在的 1:1 的 Cardinality Preservation Join。满足以下约束条件的九种 Join 都被视为 Cardinality Preservation Join,可以用于启用 View Delta Join 改写:
以 SSB 测试为例,创建以下基表:
CREATE TABLE customer (
c_custkey INT(11) NOT NULL,
c_name VARCHAR(26) NOT NULL,
c_address VARCHAR(41) NOT NULL,
c_city VARCHAR(11) NOT NULL,
c_nation VARCHAR(16) NOT NULL,
c_region VARCHAR(13) NOT NULL,
c_phone VARCHAR(16) NOT NULL,
c_mktsegment VARCHAR(11) NOT NULL
) ENGINE=OLAP
DUPLICATE KEY(c_custkey)
DISTRIBUTED BY HASH(c_custkey) BUCKETS 12
PROPERTIES (
"unique_constraints" = "c_custkey" -- 指定唯一键。
);
CREATE TABLE dates (
d_datekey DATE NOT NULL,
d_date VARCHAR(20) NOT NULL,
d_dayofweek VARCHAR(10) NOT NULL,
d_month VARCHAR(11) NOT NULL,
d_year INT(11) NOT NULL,
d_yearmonthnum INT(11) NOT NULL,
d_yearmonth VARCHAR(9) NOT NULL,
d_daynuminweek INT(11) NOT NULL,
d_daynuminmonth INT(11) NOT NULL,
d_daynuminyear INT(11) NOT NULL,
d_monthnuminyear INT(11) NOT NULL,
d_weeknuminyear INT(11) NOT NULL,
d_sellingseason VARCHAR(14) NOT NULL,
d_lastdayinweekfl INT(11) NOT NULL,
d_lastdayinmonthfl INT(11) NOT NULL,
d_holidayfl INT(11) NOT NULL,
d_weekdayfl INT(11) NOT NULL
) ENGINE=OLAP
DUPLICATE KEY(d_datekey)
DISTRIBUTED BY HASH(d_datekey) BUCKETS 1
PROPERTIES (
"unique_constraints" = "d_datekey" -- 指定唯一键。
);
CREATE TABLE supplier (
s_suppkey INT(11) NOT NULL,
s_name VARCHAR(26) NOT NULL,
s_address VARCHAR(26) NOT NULL,
s_city VARCHAR(11) NOT NULL,
s_nation VARCHAR(16) NOT NULL,
s_region VARCHAR(13) NOT NULL,
s_phone VARCHAR(16) NOT NULL
) ENGINE=OLAP
DUPLICATE KEY(s_suppkey)
DISTRIBUTED BY HASH(s_suppkey) BUCKETS 12
PROPERTIES (
"unique_constraints" = "s_suppkey" -- 指定唯一键。
);
CREATE TABLE part (
p_partkey INT(11) NOT NULL,
p_name VARCHAR(23) NOT NULL,
p_mfgr VARCHAR(7) NOT NULL,
p_category VARCHAR(8) NOT NULL,
p_brand VARCHAR(10) NOT NULL,
p_color VARCHAR(12) NOT NULL,
p_type VARCHAR(26) NOT NULL,
p_size TINYINT(11) NOT NULL,
p_container VARCHAR(11) NOT NULL
) ENGINE=OLAP
DUPLICATE KEY(p_partkey)
DISTRIBUTED BY HASH(p_partkey) BUCKETS 12
PROPERTIES (
"unique_constraints" = "p_partkey" -- 指定唯一键。
);
CREATE TABLE lineorder (
lo_orderdate DATE NOT NULL, -- 指定为 NOT NULL。
lo_orderkey INT(11) NOT NULL,
lo_linenumber TINYINT NOT NULL,
lo_custkey INT(11) NOT NULL, -- 指定为 NOT NULL。
lo_partkey INT(11) NOT NULL, -- 指定为 NOT NULL。
lo_suppkey INT(11) NOT NULL, -- 指定为 NOT NULL。
lo_orderpriority VARCHAR(100) NOT NULL,
lo_shippriority TINYINT NOT NULL,
lo_quantity TINYINT NOT NULL,
lo_extendedprice INT(11) NOT NULL,
lo_ordtotalprice INT(11) NOT NULL,
lo_discount TINYINT NOT NULL,
lo_revenue INT(11) NOT NULL,
lo_supplycost INT(11) NOT NULL,
lo_tax TINYINT NOT NULL,
lo_commitdate DATE NOT NULL,
lo_shipmode VARCHAR(100) NOT NULL
) ENGINE=OLAP
DUPLICATE KEY(lo_orderdate,lo_orderkey)
PARTITION BY RANGE(lo_orderdate)
(PARTITION p1 VALUES [("0000-01-01"), ("1993-01-01")),
PARTITION p2 VALUES [("1993-01-01"), ("1994-01-01")),
PARTITION p3 VALUES [("1994-01-01"), ("1995-01-01")),
PARTITION p4 VALUES [("1995-01-01"), ("1996-01-01")),
PARTITION p5 VALUES [("1996-01-01"), ("1997-01-01")),
PARTITION p6 VALUES [("1997-01-01"), ("1998-01-01")),
PARTITION p7 VALUES [("1998-01-01"), ("1999-01-01")))
DISTRIBUTED BY HASH(lo_orderkey) BUCKETS 48
PROPERTIES (
"foreign_key_constraints" = "
(lo_custkey) REFERENCES customer(c_custkey);
(lo_partkey) REFERENCES part(p_partkey);
(lo_suppkey) REFERENCES supplier(s_suppkey)" -- 指定外键约束。
);