分区裁剪失效有什么优化方法?
办法我不知道,但bug可以通过各个渠道反馈,希望早日修复。
OceanBase 分区裁剪(Partition Pruning)是分布式查询性能的关键环节。一旦裁剪失效,查询将退化为全分区扫描,性能急剧下降。以下从失效原因和优化方法两个维度进行系统梳理。
分区裁剪失效的常见原因
查询条件未包含分区键
这是最常见的原因。WHERE 子句中没有出现分区键列,优化器无法判断数据落在哪些分区,只能扫描全部分区。
对分区键使用了函数或表达式
-- ❌ 裁剪失效
WHERE DATE_FORMAT(order_date, '%Y-%m') = '2023-01'
-- ✅ 裁剪生效
WHERE order_date BETWEEN '2023-01-01' AND '2023-01-31'
对分区键施加函数(如 SUBSTR、DATE_FORMAT、UPPER 等)会破坏优化器的静态推导能力,导致裁剪失败。
隐式类型转换
分区键的数据类型与查询条件中的字面量类型不一致时,会触发隐式 CAST,优化器可能放弃裁剪。
-- 分区键 order_date 为 DATE 类型
-- ❌ 可能触发隐式转换
WHERE order_date = '2023-01-01 00:00:00'
-- ✅ 类型严格匹配
WHERE order_date = '2023-01-01'
分区键条件写在 JOIN 的 ON 子句中
优化器通常只对外层 WHERE 中的分区键条件做裁剪,放在 ON 子句中的条件可能不被识别。
-- ❌ 左表 t1 的分区裁剪可能失效
SELECT * FROM t1 LEFT JOIN t2 ON t1.id = t2.id AND t2.ds = '20240301'
-- ✅ 将分区键条件放在 WHERE 中
SELECT * FROM t1 LEFT JOIN t2 ON t1.id = t2.id WHERE t2.ds = '20240301'
Hash 分区上的范围查询
Hash 分区对分区键的等值查询能精确裁剪到单个分区,但范围查询(BETWEEN、>、<)无法裁剪,必须扫描所有分区。
统计信息不准确
过时的统计信息可能导致优化器做出错误的代价估算,进而选择不优的执行计划,间接影响裁剪效果。
优化方法
确保查询条件直接包含分区键
这是最根本的原则。SQL 的 WHERE 子句中必须直接出现分区键列,且不能对其施加任何函数或运算。
-- 分区键为 order_date(RANGE 分区)
-- ✅ 正确写法
SELECT * FROM orders WHERE order_date >= '2026-01-01' AND order_date < '2026-02-01';
-- ❌ 错误写法
SELECT * FROM orders WHERE MONTH(order_date) = 1;
重写 SQL,消除对分区键的函数调用
将函数从分区键一侧移到常量一侧,或使用范围条件替代。
-- ❌ 原始写法(裁剪失效)
WHERE SUBSTR(dt, 1, 6) = '202403'
-- ✅ 改写为范围查询(裁剪生效)
WHERE dt >= '20240301' AND dt < '20240401'
保持数据类型严格一致
确保查询条件中的字面量类型与分区键列类型完全匹配,避免隐式转换。
合理选择分区类型
根据业务查询模式选择合适的分区策略:
| 查询模式 | 推荐分区类型 | 说明 |
|---|---|---|
| 等值查询为主 | Hash / Key | 数据均匀打散,等值裁剪精确到单分区 |
| 范围查询为主 | Range | 支持范围裁剪,只扫描相关分区 |
| 枚举值查询 | List | 精准匹配离散值 |
| 范围 + 打散需求 | Range + Hash(二级分区) | 一级裁剪 + 二级负载均衡 |
为非分区键的高频查询创建全局索引
当业务必须按非分区键查询时,创建全局索引可以避免全分区扫描:
-- order_id 不是分区键,但需要高频查询
CREATE GLOBAL INDEX idx_order_id ON orders(order_id);
更新统计信息
定期收集表和分区级别的统计信息,帮助优化器做出更准确的决策:
-- 收集单表统计信息
CALL dbms_stats.gather_table_stats('your_db', 'orders');
-- 收集分区级统计信息
ANALYZE TABLE orders PARTITION p202601;
使用 EXPLAIN 验证裁剪效果
通过执行计划确认分区裁剪是否生效,重点关注 partitions 字段:
EXPLAIN SELECT * FROM orders WHERE order_date = '2026-01-15';
- 如果
partitions显示为单个分区(如p202601),说明裁剪生效 - 如果显示
p[0-99](全部分区),说明裁剪失效
使用二级分区兼顾裁剪与打散
对于 TP 业务,推荐 Range + Hash 的二级分区方案。一级 Range 分区支持范围裁剪,二级 Hash 分区将读写流量打散:
CREATE TABLE orders (
user_id BIGINT NOT NULL,
order_date DATE NOT NULL,
amount DECIMAL(10,2),
PRIMARY KEY (user_id, order_date)
)
PARTITION BY RANGE COLUMNS(order_date)
SUBPARTITION BY HASH(user_id) SUBPARTITIONS 8 (
PARTITION p202601 VALUES LESS THAN ('2026-02-01'),
PARTITION p202602 VALUES LESS THAN ('2026-03-01')
);
利用 Table Group 优化 JOIN 场景
多张表按相同的 Hash 键分区且分区数一致时,配合 Table Group 可以实现 Partition Wise Join,避免数据 Shuffle,同时保证分区裁剪生效:
-- 两张表按相同键 Hash 分区,放入同一 Table Group
CREATE TABLE orders (...) PARTITION BY HASH(user_id) PARTITIONS 16;
CREATE TABLE users (...) PARTITION BY HASH(user_id) PARTITIONS 16;
-- 配合 Table Group 绑定,JOIN 时同下标分区在同一个节点上
总结
分区裁剪优化的核心原则是**“让优化器能在编译期静态推导出目标分区”**。具体操作要点:
- WHERE 条件直接包含分区键,不加函数、不做运算
- 类型严格匹配,杜绝隐式转换
- 分区键条件放在 WHERE 中,而非 JOIN 的 ON 子句
- 合理选择分区类型,匹配业务查询模式
- 非分区键高频查询加全局索引
- 定期更新统计信息,确保优化器决策准确
- 用 EXPLAIN 验证裁剪效果,持续监控
多多注意SQL的写法就解决了90%的问题了。
学习下
6666
666
写得真详细,学习了。