概念介绍
分区剪枝 是指数据库在扫描分区表时,根据查询条件只扫描目标数据所在分区。通过分区剪枝可以大大减少从磁盘检索的数据量,提高查询性能。分区剪枝分为静态剪枝和动态剪枝。
分区剪枝是被动触发的,一般情况下,当查询 SQL 的条件带有分区列且能确定扫描分区时,数据库会进行分区剪枝。
如果分区列条件使用静态谓词,数据库进行静态剪枝;如果分区列条件使用绑定变量,数据库进行动态剪枝。
静态剪枝发生在SQL解析过程中,在 SQL 执行前,数据库就知道了需要扫描的分区。
实验介绍
本实验以 TPCC 业务表为例,通过分析分区表静态剪枝的基本行为、触发条件、剪枝前后行为对比,了解数据库是如何通过分区静态剪枝提升分区表的查询性能。
实验目的
了解静态剪枝的基本原理及触发条件。
了解如何判断 SQL 是否发生了静态剪枝。
了解如何合理使用静态剪枝提升分区表查询性能。
实验步骤
步骤1在数据库中创建分区表,用于验证静态剪枝功能。
GaussDB 在分布式环境下,支持 RANGE 分区表。
设置参数:
set enable_fast_query_shipping = off;
set enable_stream_operator = on;
该两个参数为会话级,只在本次会话期间生效。
使用以下语句,创建一张RANGE 分区表 bmsql_stock。
sql
DROP TABLE IF EXISTS bmsql_stock;
CREATE TABLE bmsql_stock
( s_w_id INTEGER NOT NULL,
s_i_id INTEGER NOT NULL,
s_quantity INTEGER,
s_ytd INTEGER,
s_order_cnt INTEGER,
s_remote_cnt INTEGER,
s_data VARCHAR(50),
s_dist_01 CHAR(24),
s_dist_02 CHAR(24),
s_dist_03 CHAR(24),
s_dist_04 CHAR(24),
s_dist_05 CHAR(24),
s_dist_06 CHAR(24),
s_dist_07 CHAR(24),
s_dist_08 CHAR(24),
s_dist_09 CHAR(24),
s_dist_10 CHAR(24)
)PARTITION BY RANGE(s_w_id)
(
PARTITION stock_p1 VALUES LESS THAN (35),
PARTITION stock_p2 VALUES LESS THAN (70),
PARTITION stock_p3 VALUES LESS THAN (MAXVALUE)
);
步骤2导入数据
sql
jiang=# INSERT INTO bmsql_stock SELECT generate_series(1,100),generate_series(1,999);
INSERT 0 99900
jiang=#
步骤3 通过查看打印的计划,判断一级分区是否发生静态剪枝。
剪枝是被动触发的,可以通过查看SQL 的执行计划来判断是否发生了剪枝。若只扫描了部分分区,则表明发生了剪枝行为;若扫描了全部分区,则表明未发生剪枝行为。
在打印执行计划前,执行参数设置:
set max_datanode_for_plan = 1;
该参数是会话级的,只在本次会话期间生效。
a.查看如下 SQL 的执行计划。
sql
jiang=# EXPLAIN SELECT * FROM bmsql_stock WHERE s_w_id = '59' AND s_i_id = '23';
id | operation | E-rows | E-width | E-costs
----+-----------------------------------------------+--------+---------+---------
1 | -> Streaming (type: GATHER) | 1 | 1142 | 2.33
2 | -> Partition Iterator | 1 | 1142 | 1.21
3 | -> Partitioned Seq Scan on bmsql_stock | 1 | 1142 | 1.21
(3 rows)
Predicate Information (identified by plan id)
---------------------------------------------------
2 --Partition Iterator
Iterations: 1
3 --Partitioned Seq Scan on bmsql_stock
Filter: ((s_w_id = 59) AND (s_i_id = 23))
Selected Partitions: 2
(5 rows)
表 bmsql_stock 的分区列为 s_w_id,由于查询 SQL 带有条件 s_w_id = '59',通过静态剪枝,可以只访问指定分区的数据,跳过其他分区的处理。
Iterations 值为 1,表示数据库只访问了单个分区,Selected Partitions 值为 2,表示访问了第 2 个分区。从分区定义可以看出,s_w_id = '59'落在了第 2 个分区 stock_p2,与静态剪枝结果对应。
b.将条件改成s_w_id > '59' AND s_i_id = '23',查看执行计划。
sql
jiang=# EXPLAIN SELECT * FROM bmsql_stock WHERE s_w_id > '59' AND s_i_id = '23';
id | operation | E-rows | E-width | E-costs
----+-----------------------------------------------+--------+---------+---------
1 | -> Streaming (type: GATHER) | 41 | 1142 | 516.11
2 | -> Partition Iterator | 41 | 1142 | 500.50
3 | -> Partitioned Seq Scan on bmsql_stock | 41 | 1142 | 500.50
(3 rows)
Predicate Information (identified by plan id)
---------------------------------------------------
2 --Partition Iterator
Iterations: 2
3 --Partitioned Seq Scan on bmsql_stock
Filter: ((s_w_id > 59) AND (s_i_id = 23))
Selected Partitions: 2..3
(5 rows)
可以看到,数据库基于条件 s_w_id > '59'进行静态剪枝,确定访问了分区 2 和分区 3,对应分区名为 stock_p2 和 stock_p3。
c.将条件改成 s_i_id = '23',查看执行计划。
sql
jiang=# EXPLAIN SELECT * FROM bmsql_stock WHERE s_i_id = '23';
id | operation | E-rows | E-width | E-costs
----+-----------------------------------------------+--------+---------+---------
1 | -> Streaming (type: GATHER) | 100 | 1142 | 654.05
2 | -> Partition Iterator | 100 | 1142 | 617.25
3 | -> Partitioned Seq Scan on bmsql_stock | 100 | 1142 | 617.25
(3 rows)
Predicate Information (identified by plan id)
-----------------------------------------------
2 --Partition Iterator
Iterations: 3
3 --Partitioned Seq Scan on bmsql_stock
Filter: (s_i_id = 23)
Selected Partitions: 1..3
(5 rows)
由于分区键 s_w_id 上不带有任何条件,数据库不会进行分区剪枝,会访问全部的 3 个分区。
步骤4验证静态剪枝的触发条件
分区列使用静态谓词,若符合剪枝条件,可以触发静态剪枝。
对于 RANGE 分区,符合剪枝条件的静态谓词包括范围表达式(>、>=、=、<=、<)、IN 查询,以及由此组合的布尔表达式(AND、OR);对于 LIST 分区和 HASH 分区,范围表达式仅支持等号查询,其他场景与RANGE 分区一致。需要注意的是,当分区列发生类型转换时,无法触发静态剪枝。
a.分区列发生类型转换,无法触发静态剪枝。
sql
jiang=# EXPLAIN SELECT * FROM bmsql_stock WHERE s_w_id = 59.1;
id | operation | E-rows | E-width | E-costs
----+-----------------------------------------------+--------+---------+---------
1 | -> Streaming (type: GATHER) | 500 | 1142 | 886.74
2 | -> Partition Iterator | 500 | 1142 | 700.50
3 | -> Partitioned Seq Scan on bmsql_stock | 500 | 1142 | 700.50
(3 rows)
Predicate Information (identified by plan id)
-----------------------------------------------
2 --Partition Iterator
Iterations: 3
3 --Partitioned Seq Scan on bmsql_stock
Filter: ((s_w_id)::numeric = 59.1)
Selected Partitions: 1..3
(5 rows)
实验总结
本实验通过分析分区表静态剪枝的行为、触发条件,了解数据库是如何使用静态剪枝对分区表进行查询优化的。当分区列使用静态谓词,若且符合剪枝条件,数据库可以通过静态剪枝只查找指定的分区,跳过其他分区的扫描,提高查询性能。由于静态剪枝需要分区列带有特定条件,可以通过调整表的分区方式、修改查询条件等,使得尽量多的业务场景查询能触发静态剪枝。