加速基数保留 Join
本文描述了如何通过表裁剪功能加速基数保留 Join。该功能自 v3.1 开始支持。
概述
基数保留 Join(Cardinality-preserving join)是指输出行的基数和重复因子与 Join 内部其中一个输入表的基数和重复因子相同的 Join。例如:
-
Inner Join
SELECT A.* FROM A INNER JOIN B ON A.fk = B.pk;
在以上示例中,
A.fk
(外键,Foreign Key)不为 NULL,并且引用B.pk
(主键,Primary Key)。A
中的每一行都恰好与B
中的一行匹配,因此输出的基数和重复因子与A
的相同。 -
Left Join
SELECT A.* FROM A LEFT JOIN B ON A.fk = B.pk;
此处
A.fk
引用B.pk
,但A.fk
可以包含 NULL 值。A
中的每一行最多与B
中的一行匹配。因此,输出的基数和重复因子与A
保持一致。
在此类 Join 中,如果最终输出列仅依赖于表 A
的列,表 B
的列未被使用,则可以从 Join 中裁剪表 B
。从 v3.1 开始,StarRocks 支持在基数保留 Join 中进行表裁剪,包括 CTE、逻辑视图和子查询。
用例:风险控制中的实时特征选择
基数保留 Join 的表裁剪功能在风险控制中的实时特征选择等场景中尤其有用。在这种情况下,用户需要从大量表中选择数据,通常还需要处理列和表的组合爆炸。风险控制领域常见的特点包括:
- 大量特征分布在许多独立更新的表中。
- 必须实时查看和查询最新数据。
- 使用扁平逻辑视图来简化数据模型,使列提取的 SQL 更简洁和高效。
使用扁平逻辑视图而非其他加速数据层,可以帮助用户高效地访问实时数据。在每个列提取查询中,只需要连接逻辑视图中的少量表,而非所有表。通过从这些查询中裁剪未使用的表,可以减少 Join 数量并提高性能。
功能支持
表裁剪功能支持星型模型和雪花模型中的多表连接,包括在 CTE、逻辑视图和子查询中,实现更高查询执行效率。
目前,表裁剪功能仅支持 OLAP 表和云原生表。多表连接中的外表不能被裁剪。
使用方法
以下示例使用 TPC-H 数据集。
前提条件
要使用表裁剪功能,必须满足以下条件:
- 启用表裁剪
- 设置键约束
启用表裁剪
系统默认禁用表裁剪。您需要通过配置以下 Session 变量启用此功能:
-- 启用 RBO 阶段表裁剪。
SET enable_rbo_table_prune=true;
-- 启用 CBO 阶段表裁剪。
SET enable_cbo_table_prune=true;
-- 为主键表上的 UPDATE 语句启用 RBO 阶段表裁剪。
SET enable_table_prune_on_update = true;
设置键约束
被裁剪的表必须在 LEFT Join 或 RIGHT Join 中至少有唯一键(Unique Key)或主键(Primary Key)约束。要在 INNER Join 中裁剪表,必须在唯一键或主键约束之外定义外键(Foreign Key)约束。
主键表和更新表中天然包含其隐式主键或唯一键约束。但是,对于明细表,必须手动定义唯一键约束,并确保不存在重复行。请注意,StarRocks 不会强制检查明细表的唯一键约束,仅将其视为优化提示,用于触发更激进的查询计划。
示例:
-- 在创建表时定义唯一键约束。
CREATE TABLE `lineitem` (
`l_orderkey` int(11) NOT NULL COMMENT "",
`l_partkey` int(11) NOT NULL COMMENT "",
`l_suppkey` int(11) NOT NULL COMMENT "",
`l_linenumber` int(11) NOT NULL COMMENT "",
`l_quantity` decimal64(15, 2) NOT NULL COMMENT "",
`l_extendedprice` decimal64(15, 2) NOT NULL COMMENT "",
`l_discount` decimal64(15, 2) NOT NULL COMMENT "",
`l_tax` decimal64(15, 2) NOT NULL COMMENT "",
`l_returnflag` varchar(1) NOT NULL COMMENT "",
`l_linestatus` varchar(1) NOT NULL COMMENT "",
`l_shipdate` date NOT NULL COMMENT "",
`l_commitdate` date NOT NULL COMMENT "",
`l_receiptdate` date NOT NULL COMMENT "",
`l_shipinstruct` varchar(25) NOT NULL COMMENT "",
`l_shipmode` varchar(10) NOT NULL COMMENT "",
`l_comment` varchar(44) NOT NULL COMMENT ""
) ENGINE=OLAP
DUPLICATE KEY(`l_orderkey`,`l_partkey`, `l_suppkey`)
DISTRIBUTED BY HASH(`l_orderkey`) BUCKETS 96
PROPERTIES (
"unique_constraints" = "l_orderkey,l_linenumber"
);
-- 或者可以在创建表后定义唯一键约束。
ALTER TABLE lineitem SET ("unique_constraints" = "l_orderkey,l_linenumber");
另一方面,外键约束必须显式定义。与明细表上的唯一键约束类似,外键约束也被用于优化器的提示。StarRocks 不强制检查外键约束一致性。您必须在将数据生成和导入 StarRocks 时确保数据完整性。
示例:
-- 创建在外键约束中引用的 表。请注意,被引用的列必须具有唯一键或主键约束。
-- 在此示例中,p_partkey 是表 part 的主键。
CREATE TABLE part (
p_partkey int(11) NOT NULL,
p_name VARCHAR(55) NOT NULL,
p_mfgr CHAR(25) NOT NULL,
p_brand CHAR(10) NOT NULL,
p_type VARCHAR(25) NOT NULL,
p_size INT NOT NULL,
p_container CHAR(10) NOT NULL,
p_retailprice DOUBLE NOT NULL,
p_comment VARCHAR(23) NOT NULL
) ENGINE=OLAP
PRIMARY KEY(`p_partkey`)
DISTRIBUTED BY HASH(`p_partkey`) BUCKETS 12;
-- 在创建表时定义外键约束。
CREATE TABLE `lineitem` (
`l_orderkey` int(11) NOT NULL COMMENT "",
`l_partkey` int(11) NOT NULL COMMENT "",
`l_suppkey` int(11) NOT NULL COMMENT "",
`l_linenumber` int(11) NOT NULL COMMENT "",
`l_quantity` decimal64(15, 2) NOT NULL COMMENT "",
`l_extendedprice` decimal64(15, 2) NOT NULL COMMENT "",
`l_discount` decimal64(15, 2) NOT NULL COMMENT "",
`l_tax` decimal64(15, 2) NOT NULL COMMENT "",
`l_returnflag` varchar(1) NOT NULL COMMENT "",
`l_linestatus` varchar(1) NOT NULL COMMENT "",
`l_shipdate` date NOT NULL COMMENT "",
`l_commitdate` date NOT NULL COMMENT "",
`l_receiptdate` date NOT NULL COMMENT "",
`l_shipinstruct` varchar(25) NOT NULL COMMENT "",
`l_shipmode` varchar(10) NOT NULL COMMENT "",
`l_comment` varchar(44) NOT NULL COMMENT ""
) ENGINE=OLAP
DUPLICATEK KEY(`l_orderkey`,`l_partkey`, `l_suppkey`)
DISTRIBUTED BY HASH(`l_orderkey`) BUCKETS 96
PROPERTIES (
"foreign_key_constraints" = "(l_partkey) REFERENCES part(p_partkey)"
);
-- 或者可以在创建表后定义外键约束。
ALTER TABLE lineitem SET ("foreign_key_constraints" = "(l_partkey) REFERENCES part(p_partkey)");