mysql分区表仅在分区键选对、查询条件命中分区且数据量超千万级时才有效;否则反增负担。range分区须用date字段或to_days()等确定性表达式,避免year()等函数导致裁剪失效;hash分区仅适用于等值查询,不支持范围查询。

能解决,但前提是分区键选对、查询条件能命中分区、且数据量真到了千万级以上。低于这个量级硬上分区表,反而增加维护负担、拖慢写入、干扰优化器判断。
Range 分区必须用时间字段做分区键
这是最常见也最容易踩坑的点。很多团队想按 create_time 分区,却用了 datetime 类型配合 YEAR() 或 TO_DAYS() 函数——MySQL 5.7+ 要求分区表达式必须是“确定性且可索引”的,YEAR(create_time) 在某些版本下无法被优化器用于分区裁剪。
- ✅ 正确做法:直接用
DATE类型字段(如order_date),或确保datetime字段上有前缀索引且分区表达式不带函数,例如PARTITION BY RANGE (TO_DAYS(order_time))(仅限 MySQL 5.7 兼容模式) - ❌ 错误现象:执行
EXPLAIN PARTITIONS SELECT * FROM orders WHERE create_time >= '2024-01-01'显示访问了所有分区,说明裁剪失败 - ⚠️ 注意:MySQL 8.0 对函数分区更严格,推荐统一用
DATE字段 +RANGE COLUMNS语法,比如PARTITION BY RANGE COLUMNS(order_date)
Hash 分区不适合范围查询场景
如果你的高频查询是“查某个月的所有订单”或“查最近 30 天的数据”,HASH 分区毫无帮助。它把数据打散到 N 个分区,但每次查询都得扫全部分区,失去裁剪意义。
- ✅ 适用场景:只有等值查询,比如
WHERE user_id = 12345,且user_id是分区键 - ❌ 常见误用:拿
user_id做HASH分区,却天天查WHERE create_time BETWEEN ...—— 这时哪怕加了索引,也要跨所有分区扫描 - ? 替代方案:若必须按用户维度均衡分布,又需支持时间范围查询,考虑组合分区(MySQL 8.0+ 支持
subpartition),但复杂度陡增,多数业务不如直接用RANGE+ 时间字段
分区后删旧数据别用 DELETE
这是分区表最实在的价值点:归档效率。千万级数据用 DELETE FROM t WHERE date 可能锁表几分钟甚至几小时;而 <code>ALTER TABLE t DROP PARTITION p_2022 是毫秒级元数据操作。
- ✅ 必须提前规划好分区命名规则,比如
p_2023、p_2024,方便脚本自动清理 - ⚠️ 注意:
DROP PARTITION不走 binlog 的行格式事件,主从复制中可能丢失该 DDL 的影响(尤其在 GTID 模式下需确认复制过滤规则) - ? 补充技巧:用
REORGANIZE PARTITION合并过小分区,避免分区数爆炸(MySQL 单表上限 8192 个分区,但实际建议控制在 100 个以内)
分区表不是万能加速器
它只加速能触发分区裁剪的查询。如果查询里没带分区键,或者用了函数包装(如 WHERE DATE(create_time) = '2024-06-01'),MySQL 就会扫描全部分区,性能比普通表还差——因为多了文件打开、元数据解析等开销。
- ✅ 检查裁剪是否生效:务必用
EXPLAIN PARTITIONS看partitions列是否只列出目标分区 - ⚠️ 容易被忽略的细节:联合索引中,分区键字段必须放在最左位置才能支持裁剪;否则即使写了
WHERE partition_key = ? AND other_col = ?,也可能失效 - ? 真实瓶颈常在别处:分区表上线后查询仍慢?先看
EXPLAIN是否走了索引、是否有临时表/文件排序、缓冲池是否够大——分区解决的是“扫描范围”,不是“单次扫描效率”











