MySQL 分区裁剪失效时的执行计划排查
来源:17golang原创
时间:2026-11-13 12:48:14 482浏览 收藏
MySQL 分区裁剪失效时,先不要把问题归因于“分区数量太多”。分区裁剪的目标是让优化器排除不可能命中的分区,排查重点应放在实际访问范围、分区表达式和查询条件是否保持同一语义。
官方地址:https://dev.mysql.com/doc/refman/8.4/en/partitioning-pruning.html
最短排查路径是:先看EXPLAIN输出中的partitions,再对照表的分区定义;如果日期列被DATE()等函数包住,优先改成“开始时间(含)到结束时间(不含)”的半开区间。分区裁剪只负责缩小分区访问集合,仍需要合适的索引和正确的过滤条件。
保护的资产:查询不应扫描无关分区
把一次查询看成一个需要控制的资源入口:分区集合是扫描范围,分区键是判断边界,EXPLAIN 是审计记录。风险不在于“用了分区就一定快”,而在于查询形状让优化器无法证明某些分区不可能命中,结果又回到大范围扫描。

下面用按月份划分的订单表说明排查对象。示例只展示表结构和查询入口,不把示例输出冒充为真实执行结果。
-- 分区键必须出现在 InnoDB 主键中,便于保持定义合法
CREATE TABLE orders (
id BIGINT NOT NULL,
created_at DATETIME NOT NULL,
tenant_id BIGINT NOT NULL,
amount DECIMAL(12, 2) NOT NULL,
PRIMARY KEY (id, created_at),
KEY idx_tenant_created (tenant_id, created_at)
) ENGINE = InnoDB
PARTITION BY RANGE COLUMNS (created_at) (
PARTITION p202601 VALUES LESS THAN ('2026-02-01'),
PARTITION p202602 VALUES LESS THAN ('2026-03-01'),
PARTITION pmax VALUES LESS THAN (MAXVALUE)
);
攻击路径:先用 EXPLAIN 固定事实
不要先改分区方案或增加索引。先把同一条业务查询交给 EXPLAIN,观察它实际选择了哪些分区,再按下面四列建立基线:
| 字段 | 排查问题 | 读法 |
|---|---|---|
partitions | 是否排除了无关分区 | 出现目标月份而不是整组分区,才说明范围有被缩小的迹象 |
type | 访问方式是否合理 | 结合索引与条件判断,不能单独把某个值当成结论 |
key | 是否命中了可用索引 | 分区裁剪和索引选择是两层判断 |
rows | 估算扫描量是否异常 | 用于和改写前后做相对比较,不等同于实际返回行数 |
-- 先观察计划中的 partitions、key 和 rows,不先修改表结构 EXPLAIN SELECT id, tenant_id, amount FROM orders WHERE created_at >= '2026-02-10 00:00:00' AND created_at
这里的日期范围直接落在分区键上,读计划时重点看 partitions 是否收窄到 p202602。这只是静态示例:真实分区名、估算行数和索引选择必须以目标实例返回的计划为准。
风险分级:哪些条件最容易让裁剪失去证据
把“裁剪失效”拆成三类风险,比凭经验重建分区更容易定位。
- 高风险:在分区列上包函数。 例如
DATE(created_at) = '2026-02-10'把列先变成表达式,优化器未必能把它还原为分区边界。应改成当天的左闭右开范围。 - 中风险:隐式转换或类型混用。 应用把日期、数字或时区值以不稳定的字符串形态传入,会让条件语义难以和分区表达式对应。绑定参数时保持列对应类型,并在边界处明确精度。
- 中风险:复合条件掩盖主过滤条件。 复杂的
OR、混合函数和附加业务条件可能让计划难以证明分区范围。先只保留分区键条件,再逐项加回其他条件。

-- 不把日期列包在函数里,使用当天的左闭右开范围 SELECT id, tenant_id, amount FROM orders WHERE created_at >= '2026-02-10 00:00:00' AND created_at = '2026-02-10 00:00:00' AND created_at
半开区间还有一个好处:它不会依赖“当天最后一毫秒”的精度约定。若业务字段是 DATETIME(6),仍然只需要推进到下一天的零点,不必手写一个可能遗漏精度的结束值。
防护控制:把修复写成可复用的查询契约
修复完成后,建议在 DAO 或查询构造层固定三条约束:分区列必须使用直接比较;日期范围统一使用左闭右开;其他过滤条件只能作为附加条件,不能替代分区边界。这样比在每个调用点临时拼接字符串更容易审计。
-- 这个模板把分区边界作为必填参数,其他条件保持独立 SELECT id, tenant_id, amount FROM orders WHERE created_at >= :start_time AND created_at = :start_time AND created_at
如果业务确实需要按日期取值,先在应用层把日期转换成明确的起止时间,再绑定到 :start_time 和 :end_time。不要让数据库在分区列上承担格式解析、时区推断和字符串清洗。
审计记录与回归清单
上线前后至少保留以下信息:表的分区定义、原始 SQL 形状、绑定参数的类型与时间边界、EXPLAIN 中的 partitions、key 和 rows。如果改写后 partitions 仍覆盖全部分区,就继续回到条件表达式和分区函数,不要只看总耗时下结论。
- 分区键是否直接出现在范围条件中?
- 是否有
DATE()、算术运算或隐式类型转换包裹分区列? - 是否先验证最小条件,再逐项加回
tenant_id、状态和排序条件? - 变更前后的
partitions、key、rows是否已记录并能解释?
小结
MySQL 分区裁剪排查的核心证据是执行计划里的 partitions,不是表上存在多少个分区。先对照分区定义,再优先移除分区列上的函数和隐式转换,使用明确的左闭右开范围;最后把计划字段和查询形状纳入回归记录。这样可以区分“分区访问范围没有收窄”和“范围已收窄但索引仍不合适”这两个不同问题。
相关问题
只看到一个分区就代表查询一定很快吗?
不代表。分区裁剪只说明候选分区变少,单个分区内部仍可能需要扫描大量行,仍要结合 key、rows 和过滤条件判断。
为什么不用 DATE(created_at) 写法更直观?
它把分区列包进函数,可能削弱优化器根据分区边界排除分区的能力。用当天起点和次日起点组成半开区间,边界更明确,也更适合审计计划。
改了 SQL 还要重新建分区吗?
不一定。先确认分区定义本身符合查询范围,再用 EXPLAIN 比较改写前后的访问分区;只有分区设计与业务访问模式长期不匹配时,才讨论分区调整。
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习