分区表仅在查询含分区键时有效,否则全分区扫描;RANGE分区最适时间序列数据,需配合索引与合理分区粒度。

分区表不是银弹,对千万级大表能缓解查询压力,但不能替代索引优化和字段设计;它真正起效的前提是查询条件中包含分区键,否则会扫全分区。
MySQL分区表只在查询带分区键时才有效
分区表底层是多个物理子表(table_name#P#p0、table_name#P#p1 等),但 MySQL 仍把它当一张逻辑表对外暴露。关键点在于:优化器能否根据 WHERE 条件自动裁剪掉无关分区。
- 有效场景:按
create_time分区,查询WHERE create_time BETWEEN '2025-01-01' AND '2025-06-30'→ 只扫对应月份分区 - 无效场景:同样按
create_time分区,却查WHERE user_id = 12345→ 所有分区都得扫描一遍 - 分区键必须是主键/唯一键的一部分(否则建表失败),所以常见组合是
(user_id, create_time)主键 + 按create_time分区
用 RANGE 分区处理时间序列数据最稳妥
对于日志、订单、绑定关系这类天然按时间增长的表,RANGE 分区最易理解、维护成本最低。别碰 HASH 或 KEY 分区——它们打散数据,让范围查询失效,且后期无法灵活 drop 旧分区。
- 建表时明确指定
PARTITION BY RANGE (TO_DAYS(create_time)),避免用函数如YEAR()(会导致无法使用ADD PARTITION) - 每个分区覆盖 1–3 个月数据,太细(如按天)会导致分区数爆炸(超 8192 个会报错),太粗(如按年)则裁剪效果差
- 定期用
ALTER TABLE t DROP PARTITION p202401清理归档数据,比DELETE快得多,且不锁全表
分区后仍要为高频查询字段单独建索引
分区只是把大表拆成小表,每个子表仍是独立 B+ 树结构。如果子表内数据量仍有百万级,没索引照样慢。
- 不要以为“已经分区了就不用索引”,
user_id、status、business_type这类高频过滤字段,必须在每个子表上都有对应索引 - 复合索引顺序仍要遵循最左前缀原则,例如查询常带
WHERE create_time > ? AND status = ?,索引应为(create_time, status)而非反过来 - 用
EXPLAIN PARTITIONS替代普通EXPLAIN,确认是否只访问目标分区,以及是否用了索引
分区表的隐形代价容易被低估
分区表不是免费的午餐。它会让某些操作变得更重,尤其在运维层面。
- 备份恢复更复杂:
mysqldump默认导出整个逻辑表,无法单分区 dump;物理备份需单独处理每个.ibd文件 - DDL 操作可能锁全表:即使只改一个分区的结构(如加列),InnoDB 仍可能触发全表重建(取决于 MySQL 版本和
ALGORITHM设置) - 监控难度上升:
information_schema.PARTITIONS查分区大小不准,SHOW TABLE STATUS只显示总行数,真实数据分布需逐分区查
真正该优先做的,其实是把 SELECT * 改成明确字段、把 OR 拆成 IN、把 LIKE '%abc' 改成全文索引或前置冗余字段——这些改动零风险、见效快,而分区一旦选错策略,后续迁移成本极高。



















