当前位置: 首页 > article >正文

深入解析 StarRocks 物化视图:全方位的查询改写机制

小编导读:

本文将重点介绍如何利用物化视图进行查询改写。文章将全面介绍物化视图的基本原理、关键特性、应用案例、使用场景、代码细节以及主流大数据产品的物化视图改写能力对比。

物化视图在 StarRocks 中扮演着至关重要的角色,它是进行数据建模和加速查询的神器。特别是在 BI 场景中,通过预先计算 Join 和 Aggregation 操作,物化视图不仅能大幅度提升查询性能,还能显著降低存储成本。

使用物化视图通常包含以下三个阶段:

  1. 设计与创建:首先,我们需要仔细分析查询的特点,以选择构建最适合的物化视图。

  2. 视图维护:当基础表的数据发生变化时,物化视图也需要及时更新,以确保数据的一致性。

  3. 查询改写:利用预计算的数据,物化视图能有效地加速查询处理过程,提供更快的响应速度。

本文重点讨论物化视图的第三阶段:如何利用物化视图进行查询改写。StarRocks 的异步物化视图采用了广泛认可的 SPJG(Select-Projection-Join-Groupby)算法。这允许系统在用户无需修改任何查询的前提下,自动将原始查询转换为对物化视图的查询。借助物化视图中预计算的结果,这种自动化的查询改写大幅降低了计算代价,从而实现了显著的查询加速。

在典型的 OLAP 标准测试集中, 通过创建物化视图可以显著提升查询性能:

  • SSB 100GB:与传统的星形模型相比,物化视图能将总体查询耗时减少至原来的 1/3。

  • TPC-H 100GB:这种技术能加速一半的查询,平均耗时降至原来的 1/5。

基本原理

(StarRocks 物化视图改写流程)

物化视图改写的关键流程如上图所示,整体上可以划分为以下三个阶段:

  1. 预处理:在 Analyzer 处理后得到的逻辑计划树的基础上,系统会识别出相关的物化视图候选集。此阶段还包括过滤掉无法用于改写的物化视图,以缩小搜索空间并确保物化视图数据的新鲜度。

  2. SPJG 物化视图改写:应用基于cost-based的 SPJG 物化视图改写规则,自动遍历搜索空间中可用于改写的子树,并尝试进行改写,并且最终会根据 Cost Model 选择最优的改写方案。

  3. 后处理:对物化视图改写后的 Plan 执行列裁剪、谓词下推、分区裁剪等优化操作,以提升改写后 Plan 的执行性能。

举例说明,对于一条具体的查询,物化视图的改写可以分为以下几个步骤处理:

  1. 预处理:

  • 分析访问表:首先分析查询涉及的基表, 根据这些基表与物化视图之间的依赖关系,识别可能有用的物化视图

  • 候选视图筛选:对于复杂的查询可能存在大量的候选物化视图,直接考虑所有视图会导致计算开销过大。因此,因此,需要根据视图的“适用性”对候选视图进行排序,并选择一个子集进行进一步分析

  • 新鲜度验证:检查候选物化视图的数据新鲜度,若物化视图数据不满足查询的要求,则抛弃这些视图

  1. TEXT 改写

  • 当查询与某个物化视图在 AST tree/语法树结构上完全一致时,可以通过文本匹配直接将查询改写为对该视图的访问

  1. SPJG 查询改写

  • SPJG 改写适用于查询与物化视图有所差异的场景,可以对物化视图进行补偿改写,提供了更大的灵活性,但实现上也更为复杂

  • 在 SQL 优化器中应用多种规则来匹配视图和查询,对所有可能的 Query Plan 进行改写,这一过程的计算开销相对较高

  • 改写后,对于产生的所有可能的改写结果,使用 Cost Model 来评估并选择最优的改写方案

  1. 后置处理

  • 对改写后的查询计划应用更多优化器规则,如列裁剪、谓词下推和分区裁剪等

select sum(lo_revenue) as lo_revenue, d_year, p_brand
from lineorder
join dates on lo_orderdate = d_datekey
join part on lo_partkey = p_partkey
join supplier on lo_suppkey = s_suppkey
where p_category = 'MFGR#12' and s_region = 'AMERICA'
group by d_year, p_brand
order by d_year, p_brand;

关键特性

除核心能力之外,StarRocks 物化视图自动改写还包括以下关键特性:

  1. 数据一致性:

  • 内部表一致性:确保物化视图改写的结果与查询原始表结果完全一致,实现强数据一致性。

  • 过期数据处理:支持配置数据过期容忍时间,适应数据频繁变更的场景,通过 staleness 改写技术应对数据变化。

  1. 复杂查询支持

  • 多表 Join 支持:支持各种类型的 join,包括 view delta join 和 join derivability rewrite 等复杂 join 场景的改写,优化大宽表查询。

  • 聚合查询加速:通过聚合改写技术加速聚合查询,提升报表查询性能。

  • 嵌套视图改写:支持嵌套物化视图改写,解决复杂查询的改写问题,扩展改写范围。

  • 复杂表达式支持:够处理包括函数调用和四则运算在内的复杂表达式,满足复杂的分析计算需求

  1. 实时数据融合

  • 新鲜数据查询加速:利用 union 改写和 TTL 功能联合使用,加速新鲜数据查询,并实现历史数据自动回查原表。

  1. 多数据源支持

  • 逻辑视图物化:允许在逻辑视图上创建物化视图,支持基于 view 建模的场景下的查询加速

  • 外部表物化视图:支持包括 Hive、Iceberg、Hudi、DeltaLake、Paimon、JDBC(MySQL Dialet)等,提升数据湖场景下的查询性能

应用案例

在携程的 BI 平台中,通过 StarRocks MV 实现了几方面的性能加速:

  • Projection MV:在 Hive Table 基础上创建 Projection MV,发挥 StarRocks 内表和存储引擎的性能,使得关键报表能够获得几倍的性能加速。由于 StarRocks MV 能够支持 Hive Table 的自动刷新,一次创建后几乎不需要后续的维护,从而大幅降低 ETL 的维护成本

  • 嵌套视图:在 MV 的基础上,对复杂查询创建嵌套视图,以进一步加速关键报表查询。这些查询通常涉及 BI 场景中的典型操作,如 Join、Aggregation、多层聚合等复杂查询。

  • AutoMV:利用 AutoMV 能力,分析慢查询自动推荐出合适的物化视图,从而进一步减少了人工维护成本。

使用场景

Join Rewrite

StarRocks 支持 join 查询改写,支持的 join 类型包括:Inner join/cross join/left outer join/full outer join/right outer join/semi join/anti join。

下面是一个 join mv 改写的例子,建表如下:

CREATE TABLE `customer` (
  `c_custkey` int(11) NOT NULL COMMENT "",
  `c_name` varchar(26) NOT NULL COMMENT "",
  `c_address` varchar(41) NOT NULL COMMENT "",
  `c_city` varchar(11) NOT NULL COMMENT "",
  `c_nation` varchar(16) NOT NULL COMMENT "",
  `c_region` varchar(13) NOT NULL COMMENT "",
  `c_phone` varchar(16) NOT NULL COMMENT "",
  `c_mktsegment` varchar(11) NOT NULL COMMENT ""
) ENGINE=OLAP
DUPLICATE KEY(`c_custkey`)
COMMENT "OLAP"
DISTRIBUTED BY HASH(`c_custkey`) BUCKETS 12
PROPERTIES (
"replication_num" = "1"
);

CREATE TABLE `lineorder` (
  `lo_orderkey` int(11) NOT NULL COMMENT "",
  `lo_linenumber` int(11) NOT NULL COMMENT "",
  `lo_custkey` int(11) NOT NULL COMMENT "",
  `lo_partkey` int(11) NOT NULL COMMENT "",
  `lo_suppkey` int(11) NOT NULL COMMENT "",
  `lo_orderdate` int(11) NOT NULL COMMENT "",
  `lo_orderpriority` varchar(16) NOT NULL COMMENT "",
  `lo_shippriority` int(11) NOT NULL COMMENT "",
  `lo_quantity` int(11) NOT NULL COMMENT "",
  `lo_extendedprice` int(11) NOT NULL COMMENT "",
  `lo_ordtotalprice` int(11) NOT NULL COMMENT "",
  `lo_discount` int(11) NOT NULL COMMENT "",
  `lo_revenue` int(11) NOT NULL COMMENT "",
  `lo_supplycost` int(11) NOT NULL COMMENT "",
  `lo_tax` int(11) NOT NULL COMMENT "",
  `lo_commitdate` int(11) NOT NULL COMMENT "",
  `lo_shipmode` varchar(11) NOT NULL COMMENT ""
) ENGINE=OLAP
DUPLICATE KEY(`lo_orderkey`)
COMMENT "OLAP"
DISTRIBUTED BY HASH(`lo_orderkey`) BUCKETS 48
PROPERTIES (
"replication_num" = "1"
);

基于上述的表,构建物化视图

-- MV
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;

则如下的查询可以被改写为查询join_mv1:

-- Query
select lo_orderkey, lo_linenumber, lo_revenue, c_name, c_address
from lineorder inner join customer
on lo_custkey = c_custkey;

在 join 中,select 中支持复杂表达式改写,比如四则运算,string 函数,日期函数处理,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;

上述的 join 改写场景是 Query 的 join 类型和表集合同 MV 相同的场景,StarRocks 中还扩展支持了以下几种 join 场景的改写。

1.1 Query delta join rewrite

query delta join 就是指查询的 join 表是物化视图中 join 表的超集场景。比如如下 query 是 lineorder/customer/part三表 join,join_mv1 只有 lineorder/customer 两表 join,StarRocks 支持将 query 改写到 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 

改写之后的 plan 如下:

1.2 View delta join rewrite

View delta join 指的是在查询中,涉及的 join 表是物化视图中 join 表的子集。这种场景的改写能力通常适用于大宽表查询。例如,在 SSB 场景中,可以构建一个包含所有表的物化视图,将多个表 join 成一个大宽表。这样,所有 SSB 查询都可以通过物化视图的透明改写来提升查询性能。测试结果表明,通过物化视图改写后的多表 join 查询,其性能可达到直接查询大宽表的水平。

为了实现 view delta join 的改写,要求物化视图中的 join 必须与查询中的 join 具有1:1的 cardinality preservation(基数保持)关系。以下是 SSB 改写的示例。在满足下列的 join 条件时,都可以进行 cardinality preservation join 改写。任何满足其中一种条件的 join,都能够进行 view delta join 的改写。

在 StarRocks 中, 可以用以下语法指定主外键关系:

CREATE TABLE `customer` (...) 
PROPERTIES (
    "unique_constraints" = "c_custkey" #指定唯一键
);

CREATE TABLE `lineorder` (...) 
PROPERTIES (
    "foreign_key_constraints" = "(lo_custkey) REFERENCES customer(c_custkey);(lo_partkey) REFERENCES part(p_partkey);(lo_suppkey) REFERENCES supplier(s_suppkey)" #指定外键约束
);

对于 SSB 中的 Query,往往不会查询 MV 的所有表,但是通过指定了主外键关系,仍然能够利用物化视图改写加速,以其中一个查询为例:

--MV
CREATE MATERIALIZED VIEW lineorder_flat_mv
DISTRIBUTED BY HASH(LO_ORDERDATE, LO_ORDERKEY) BUCKETS 48
partition by LO_ORDERDATE
REFRESH manual
PROPERTIES (
    "replication_num" = "1"
)
AS SELECT
       *
   FROM lineorder AS l
       INNER JOIN customer AS c ON c.C_CUSTKEY = l.LO_CUSTKEY
       INNER JOIN supplier AS s ON s.S_SUPPKEY = l.LO_SUPPKEY
       INNER JOIN part AS p ON p.P_PARTKEY = l.LO_PARTKEY
       INNER JOIN dates AS d ON l.LO_ORDERDATE = d.D_DATEKEY;    
-- Query
select sum(lo_revenue) as lo_revenue, d_year, p_brand
from lineorder
join dates on lo_orderdate = d_datekey
join part on lo_partkey = p_partkey
join supplier on lo_suppkey = s_suppkey
where p_category = 'MFGR#12' and s_region = 'AMERICA'
group by d_year, p_brand
order by d_year, p_brand;

1.3 Join derivability rewrite

Join 派生改写是在物化视图(MV)的 JOIN 类型与查询(query)不一致,但 MV 的结果包含查询的结果时,进行的改写,例如 MV 使用了 OUTER JOIN,而查询是 INNER JOIN。目前分为以下两种情况:

  1. 两表 join 的情况:此时会枚举所有 JOIN 顺序和多种 JOIN 方式,检查 INNER/SEMI/ANTI/OUTER 之间是否兼容,在兼容的情况下仍然能够进行改写

  2. 三表或三表以上的 join:多表时无法枚举所有可能性,因此只做相对严格的兼容性检查

举个例子,MV 使用了 OUTER JOIN,而 Query 是 INNER JOIN, 直接改写会导致结果中包含 NULL,此时 StarRocks 会补偿一个谓词 IS NOT NULL 去保证结果的正确性。

-- MV
create materialized view join_mv3 
distributed by hash(`lo_orderkey`)
as
select lo_orderkey, lo_linenumber, c_name, sum(lo_revenue) as total_revenue, max(lo_discount) as max_discount 
from lineorder 
left join customer
on lo_custkey = c_custkey
group by lo_orderkey, lo_linenumber, c_name;

-- Query
select lo_orderkey, lo_linenumber, c_name, sum(lo_revenue) as total_revenue, max(lo_discount) as max_discount 
from lineorder 
join customer
on lo_custkey = c_custkey
group by lo_orderkey, lo_linenumber, c_name;

Aggregation Rewrite

支持多表聚合查询的改写,并且支持所有的聚合函数,其中包括 bitmap_union/hll_union/percentile_union 等。

-- MV
create materialized view agg_mv1
distributed by hash(`lo_orderkey`)
as
select lo_orderkey, lo_linenumber, c_name, sum(lo_revenue) as total_revenue, max(lo_discount) as max_discount 
from lineorder inner join customer
on lo_custkey = c_custkey
group by lo_orderkey, lo_linenumber, c_name;

如下查询可被 agg_mv1 改写

-- Query
select lo_orderkey, lo_linenumber, c_name, sum(lo_revenue) as total_revenue, max(lo_discount) as max_discount 
from lineorder inner join customer
on lo_custkey = c_custkey
group by lo_orderkey, lo_linenumber, c_name;

除此最基础的场景之外, 还有一些扩展的场景。

2.1 Rollup

同时,支持聚合物化视图的上卷改写,例如当查询中的 GROUP BY 比 MV 的 GROUP BY 更少时,能够一定程度上服用 MV 的结果,但是仍然需要做二次聚合,才能得到最终结果:

2.2 Count distinct

Count distinct 计算一般应用于精确去重的场景。与普通的聚合不同在于,它通常无法利用上卷进行改写。

但是 StarRocks 支持通过 bitmap 来实现 count distinct 改写,进而实现基于物化视图的高性能精确去重:

  • 创建物化视图时,使用 bitmap_union(to_bitmap(lo_custkey))

  • 查询时,仍然使用普通的 count(distinct lo_custkey) 即可

-- MV
create materialized view distinct_mv
distributed by hash(`lo_orderkey`)
as
select lo_orderkey, bitmap_union(to_bitmap(lo_custkey)) as distinct_customer
from lineorder
group by lo_orderkey;

-- Query
select lo_orderkey, count(distinct lo_custkey) from lineorder group by lo_orderkey;

Nested mv rewrite

StarRocks 支持嵌套物化视图改写。比如有如下三个物化视图,agg_mv2 是基于物化视图 join_mv2 之上构建的物化视图;agg_mv3 是基于 agg_mv2 之上构建的物化视图。通过这种方式,能够有效处理复杂的多层子查询。

Union rewrite

Union Rewrite 指的是物化视图的数据是查询的子集,仍然能够进行改写:

  • Partial Predicate:MV 谓词范围是查询的子集,此时会将差集回原表查询,再 UNION 起来

  • Partial Partition:MV 只物化了部分 Partition,此时其余的 Partition 回原表查询

4.1 Partial predicate

例如 MV 中有谓词 where lo_orderkey < 300000000

-- MV
create materialized view agg_mv4
distributed by hash(`lo_orderkey`)
as
select lo_orderkey, sum(lo_revenue) as total_revenue, max(lo_discount) as max_discount 
from lineorder
where lo_orderkey < 300000000
group by lo_orderkey;

则如下的查询会被改写:其中,agg_mv5 包含 lo_orderkey < 300000000 的数据,lo_orderkey >= 300000000 的数据通过直接读取lineorder表进行计算,最终通过union之后再聚合,获取最终结果。

-- Query
select lo_orderkey, sum(lo_revenue) as total_revenue, max(lo_discount) as max_discount 
from lineorder
group by lo_orderkey;

4.2 Partial partition

针对分区表来说,如果基于分区表构建分区物化视图,查询的分区范围是物化视图的最新分区范围的超集,查询会被 union 改写。

比如,有如下的物化视图, base 表 lineorder 的目前包含 p1-p7 分区,物化视图目前也包括 p1-p7 分区。

-- MV
create materialized view agg_mv5
distributed by hash(`lo_orderkey`)
partition by range(`lo_orderdate`)
refresh manual
as
select lo_orderdate, lo_orderkey, sum(lo_revenue) as total_revenue, max(lo_discount) as max_discount 
from lineorder
group by lo_orderkey;

如果 lineorder 新增一个 p8 分区,分区范围是[("19990101"), ("20000101")),则下面的查询会被改写为 union:

-- Query
select lo_orderdate, lo_orderkey, sum(lo_revenue) as total_revenue, max(lo_discount) as max_discount 
from lineorder
group by lo_orderkey;

其中,agg_mv5 包含 p1-p7 分区的数据,p8 分区的数据通过直接读取 lineorder 表进行计算,最终通过 union 之后再聚合,获取最终结果。

MV on views

支持从 view 上创建 MV,并且查询 view 的时候能够实现透明改写。在查询改写时会有两种方式:

  • VIEW 展开:内联整个 VIEW,当做普通的 QUERY 改写

  • VIEW 独立:将 VIEW 作为独立的算子,不考虑内容,再进行改写

比如有如下的 view

-- View
create view customer_view1 as
select c_custkey, c_name, c_address
from customer;

-- View
create view lineorder_view1 as
select lo_orderkey, lo_linenumber, lo_custkey, lo_revenue
from lineorder;

则可以构建如下的物化视图,在改写的时候,MV 上的 view 会被自动展开到 view 引用的 base 表上,然后进行透明匹配改写。

-- MV
create materialized view join_mv1
distributed by hash(`lo_orderkey`)
as
select lo_orderkey, lo_linenumber, lo_revenue, c_name
from lineorder_view1 inner join customer_view1
on lo_custkey = c_custkey;

MV on External catalog

StarRocks 支持在 Hive/Hudi/Iceberg/Paimon/DeltaLake/JDBC 外表上构建物化视图,并且能够进行透明改写。上述所有的改写能力大部分在外表物化视图中都支持,具体支持程度可参考使用文档。

StarRocks 在物化视图改写上,目前还有如下限制:

  1. 不支持非确定性函数的改写,包括RAND/RANDOM/UUID/SLEEP等

  2. 在SPJG改写模式下,不支持窗口分析函数的改写;基于文本的改写,不受这个限制

  3. 在SPJG改写模式下,如果mv定义语句中包含limit/order by/union/except/intersect/minus/grouping sets/with cube/with rollup,则无法用于改写;基于文本的改写,不受这个限制

  4. 部分外表(Hudi/DeltaLake)上还不支持查询结果的强一致

能力对比

以下列出主流大数据产品在物化视图上的改写能力:

图片

总结

本文主要介绍了 StarRocks 中物化视图查询改写的技术原理,从优化器的执行流程,到对不同查询的处理 Join、Aggregation、View、Union 等,以及内部视角的反省和外部视角的对比。希望本文能够对关心技术原理的读者有所帮助,对 StarRocks 的用户带来更多的技术洞察和业务启发

参考文献:

  1. Optimizing Queries Using Materialized Views: A Practical, Scalable Solution

  2. Materialized view in Apache calcite: https://calcite.apache.org/docs/materialized_views.html

  3. Oracle:https://docs.oracle.com/en/database/oracle/oracle-database/12.2/dwhsg/advanced-query-rewrite-materialized-views.html#GUID-0906CA6B-7EE3-42E1-A598-C6541BCD9B36

延伸阅读:

StarRocks 物化视图:指标平台性能提升的新引擎

重新定义物化视图,你必须拥有的极速湖仓神器!

QPS 提升 10 倍!滴滴借助 StarRocks 物化视图实现低成本精确去重

StarRocks 技术内幕 | 多表物化视图的设计与实现

更多交流,联系我们:StarRocks


http://www.kler.cn/a/445452.html

相关文章:

  • 【网络云计算】2024第51周-每日【2024/12/17】小测-理论-解析
  • 源码分析之Openlayers中OverviewMap鹰眼控件
  • 王佩丰24节Excel学习笔记——第十二讲:match + index
  • Ubuntu本地化安装MYSQL及Navicat
  • 【Python】基于Python的CI/CD工具链:实现自动化构建与发布
  • LabVIEW与PLC点位控制及OPC通讯
  • 【练习Day17】寻找第 K 大
  • Linux缓存管理:如何安全地清理系统缓存
  • 【Mars3d】设置backgroundImage、map.scene.skyBox、backgroundImage来回切换
  • 几种广泛使用的 C++ 编译器
  • 广东省佛山市南海信息学竞赛高频考查点系列全解
  • 基于开源 AI 智能名片 S2B2C 商城小程序的智慧零售仓储管理创新策略研究
  • 简述 React 的生命周期
  • MATLAB转换C语言--问题(一)FFT 和 IFFT 的缩放因子
  • 微服务网关初体验
  • 【Java基础面试题025】什么是Java的Integer缓存池?
  • C++ 字符串(string)使用
  • Unity-Editor扩展GUI基本实现一个可拖拉放的格子列表
  • 32单片机串口数据接收、空闲IDLE中断详解
  • 【渗透技术总结】SQL手工注入总结
  • SQL进阶技巧:如何根据工业制程参数计算良品率?
  • 【学习笔记】深入浅出详解Pytorch中的View, reshape, unfold,flatten等方法。
  • hadoop技术栈的基本启停命令
  • C05S12-MySQL数据库事务
  • Day9 神经网络的偏导数基础
  • bacnet4j-5.0.2.jar资源