登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  数据库 >  MySQL

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 是审计记录。风险不在于“用了分区就一定快”,而在于查询形状让优化器无法证明某些分区不可能命中,结果又回到大范围扫描。

MySQL 分区表、分区键、查询条件与执行计划之间的静态关系说明图
图1:分区键、查询条件和执行计划的静态结构说明图,不是运行截图。

下面用按月份划分的订单表说明排查对象。示例只展示表结构和查询入口,不把示例输出冒充为真实执行结果。

-- 分区键必须出现在 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、混合函数和附加业务条件可能让计划难以证明分区范围。先只保留分区键条件,再逐项加回其他条件。
MySQL 分区裁剪从日期函数和隐式转换到 partitions 计划字段的风险关系说明图
图2:查询表达式风险与 partitions 计划字段之间的静态关系说明图,不是运行截图。
-- 不把日期列包在函数里,使用当天的左闭右开范围
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 比较改写前后的访问分区;只有分区设计与业务访问模式长期不匹配时,才讨论分区调整。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>