分区加速的前提是WHERE条件严格匹配分区字段且无函数包装,否则触发全表扫描;正确写法为直接使用分区字段比较,避免DATE()等函数;聚合需配合本地索引与显式分区指定;二级分区仅在两级条件同时满足时生效;INSERT必须确保dt合法,否则落入默认分区拖慢查询。

分区字段必须和WHERE条件严格对齐
分区表加速的前提是查询能精准命中分区,否则会全表扫描。比如按 dt 字段做 RANGE 分区(每天一个分区),但查询写成 WHERE date_format(create_time, '%Y-%m-%d') = '2024-05-01',MySQL 无法下推到分区层——因为 date_format() 是非确定性函数,优化器放弃分区裁剪。
正确做法是:WHERE 条件直接使用分区字段,且不加函数包装:
- ✅
WHERE dt = '2024-05-01' - ✅
WHERE dt BETWEEN '2024-05-01' AND '2024-05-07' - ❌
WHERE DATE(create_time) = '2024-05-01'(即使create_time和dt值相同) - ❌
WHERE YEAR(dt) = 2024(范围过大,可能跨多个分区但无法精确裁剪)
聚合函数要避免跨分区的隐式排序或去重
像 SELECT COUNT(DISTINCT user_id) FROM t PARTITION (p20240501) 看似只查单分区,但如果实际执行计划里出现 Using temporary; Using filesort,说明 MySQL 在单分区内部仍做了临时表操作——这通常是因为 user_id 没有索引,或数据倾斜导致内存不足被迫落盘。
实操建议:
- 在分区表上为高频聚合字段(如
user_id、status)建立本地索引(LOCAL),而非全局索引 - 避免在聚合中混用未分区字段,例如
GROUP BY from_channel而from_channel未出现在分区键中,可能导致每个分区都生成中间结果再合并 - 若需跨多天统计 UV,优先用
UNION ALL显式指定多个分区,比直接WHERE dt IN (...)更可控
MySQL 8.0+ 的 SUBPARTITION 对日期+状态组合查询帮助有限
有人想用 LIST-RANGE 二级分区,比如一级按 dt,二级按 status,期望快速查「5月1日所有 status=2 的记录」。但实际执行中,MySQL 仍需先定位到 p20240501,再在该分区内扫描所有子分区——SUBPARTITION 并不改变主分区的 I/O 范围,只是逻辑组织方式。
真正有效的是让 WHERE 同时满足两级分区键:
- ✅
WHERE dt = '2024-05-01' AND status = 2(可精准定位到一个子分区) - ❌
WHERE dt = '2024-05-01'(仍扫描该主分区下全部子分区) - ❌
WHERE status = 2(全表扫描,完全失效)
多数场景下,单级 RANGE 分区 + 合理索引,比强行套二级分区更稳定。
INSERT 时没走对应分区,会导致查询变慢
分区表插入数据时,如果 dt 字段为 NULL 或非法值(如 '0000-00-00'),MySQL 会把这条记录塞进 default 分区(或最大分区),后续查某天数据时,优化器虽跳过其他分区,却仍要扫描这个“垃圾分区”——尤其当它累积了大量脏数据,就会拖慢整体响应。
必须检查:
- INSERT 语句是否显式赋值
dt,而不是依赖 DEFAULT 或触发器延迟填充 - 应用层写入前校验
dt是否为有效日期,拒绝非法值 - 定期运行
SELECT * FROM t PARTITION (p_default)(或查看INFORMATION_SCHEMA.PARTITIONS中TABLE_ROWS异常高的分区)
分区不是银弹,它只加速“能精准切分+合理过滤”的查询;一旦写入失控或查询条件偏移,性能反而比普通表更差。










