分区裁剪(Partition Pruning)是SelectDB查询优化器的核心能力之一。通过在查询条件中包含分区列的过滤条件,优化器能自动跳过不相关的分区,大幅减少数据扫描量。本文介绍分区裁剪的原理、生效条件和优化方法。
分区设计要点
分区裁剪能否生效,取决于建表时的分区设计。合理的分区策略是裁剪优化的前提。
选择合适的分区列
-
选择查询中频繁出现在WHERE条件中的列作为分区列,通常为时间列(如
event_time、order_date)。 -
分区列应具有较高的区分度,使查询条件能有效缩小扫描范围。
使用AUTO PARTITION自动分区(推荐)
通过AUTO PARTITION在数据写入时按需自动创建分区,无需预创建,适合分区键取值范围不确定或分区粒度灵活的场景:
-- 自动分区示例:写入时按天自动创建分区,无需预创建
CREATE TABLE user_events (
event_time DATETIME NOT NULL,
user_id BIGINT NOT NULL,
event_type VARCHAR(32),
event_data TEXT
)
DUPLICATE KEY(event_time, user_id)
AUTO PARTITION BY RANGE (date_trunc(event_time, 'day')) ()
DISTRIBUTED BY HASH(user_id) BUCKETS AUTO;
使用动态分区管理生命周期
对于分区生命周期管理有明确需求的时序数据(如保留最近N天分区、自动清理过期分区),配置动态分区:
-- 动态分区示例:自动管理分区生命周期(保留最近30天,预创建未来3天)
CREATE TABLE user_events_dynamic (
event_time DATETIME NOT NULL,
user_id BIGINT NOT NULL,
event_type VARCHAR(32),
event_data TEXT
)
DUPLICATE KEY(event_time, user_id)
PARTITION BY RANGE(event_time) ()
DISTRIBUTED BY HASH(user_id) BUCKETS AUTO
PROPERTIES (
"dynamic_partition.enable" = "true",
"dynamic_partition.time_unit" = "DAY",
"dynamic_partition.start" = "-30",
"dynamic_partition.end" = "3",
"dynamic_partition.prefix" = "p"
);
控制分区粒度
单表分区数建议在数千以内。过细的分区(如按秒)会增加元数据管理开销;过粗的分区(如按年)会降低裁剪效果,导致单次查询仍需扫描大量数据。时序数据通常按天或月分区。
分区裁剪原理
SelectDB的分区裁剪在查询计划生成阶段进行。优化器根据WHERE条件中对分区列的过滤表达式,确定需要扫描的分区集合:
-
Range分区:优化器通过比较过滤条件与分区范围来确定命中的分区。
-
List分区:优化器通过过滤条件中的等值或IN条件匹配分区值列表。
可以通过EXPLAIN查看查询计划中的partitions字段确认分区裁剪是否生效:
EXPLAIN SELECT * FROM orders WHERE order_date = '2024-01-15';
-- 查看输出中的 partitions 字段
-- partitions=1/30 表示从30个分区中只扫描了1个分区
生效条件
分区裁剪在以下条件下生效:
|
条件类型 |
是否生效 |
示例 |
|
等值过滤 |
是 |
|
|
范围过滤 |
是 |
|
|
IN列表 |
是 |
|
|
函数包裹分区列 |
否 |
|
|
隐式类型转换 |
可能不生效 |
|
|
OR组合非分区列 |
可能不生效 |
|
优化建议
直接使用分区列作为过滤条件
-- 推荐:直接使用分区列
SELECT * FROM orders WHERE order_date >= '2024-01-01' AND order_date < '2024-02-01';
-- 不推荐:函数包裹分区列导致无法裁剪
SELECT * FROM orders WHERE DATE_FORMAT(order_date, '%Y-%m') = '2024-01';
确保过滤值类型与分区列一致
-- 推荐:使用与分区列一致的DATE类型
SELECT * FROM orders WHERE order_date = DATE '2024-01-15';
-- 避免:传入字符串可能导致隐式转换
SELECT * FROM orders WHERE order_date = 20240115;
Join查询中的分区裁剪
在Join查询中,确保每张分区表的过滤条件都包含分区列:
-- 两张表都能进行分区裁剪
SELECT a.*, b.product_name
FROM orders a JOIN products b ON a.product_id = b.product_id
WHERE a.order_date >= '2024-01-01'
AND a.order_date < '2024-02-01'
AND b.category = 'electronics';
验证分区裁剪效果
使用EXPLAIN VERBOSE查看详细的分区裁剪信息:
EXPLAIN VERBOSE SELECT count(*) FROM orders WHERE order_date = '2024-01-15';
-- 关注输出中:
-- TABLE: orders(orders), PREAGGREGATION: ON
-- partitions=1/365 -- 从365个分区中裁剪到1个
-- tablets=10/3650 -- 对应tablet数量也大幅减少
如果partitions显示的分区数等于总分区数,说明分区裁剪未生效,需要检查WHERE条件是否满足生效条件。