结论:优先用 PARTITION BY RANGE (TO_DAYS()),因其自动分区裁剪、运维成本低、边界清晰;手动分表易导致 JOIN/统计/DDL 问题,且 YEAR()*100+MONTH() 会造成分区不连续和边界错误。

直接说结论:按时间字段做 RANGE 分区(推荐用 TO_DAYS())能显著加速历史数据查询,但前提是查询条件必须包含分区键、不能包裹函数、且主键或唯一索引必须包含该字段——否则分区完全失效。
为什么 WHERE create_time >= '2024-01-01' 有时还是全表扫描?
MySQL 的分区剪枝(partition pruning)只在满足严格条件时触发。常见失效场景包括:
-
WHERE YEAR(create_time) = 2024:函数包裹分区键,优化器无法推导分区边界,partitions: NULL -
WHERE DATE_FORMAT(create_time, '%Y-%m') = '2024-01':同上,彻底失去剪枝能力 - 分区字段未包含在主键或任何唯一索引中:MySQL 5.7+ 会拒绝建表,低版本虽能建但查询不稳定
- 查询只用
WHERE user_id = 123,而分区键是create_time:所有分区都得扫一遍,还多一层元数据开销
验证是否生效,必须用:EXPLAIN PARTITIONS SELECT * FROM order_history WHERE create_time >= '2024-01-01',看 partitions 列是否只列出几个分区名。
建表时怎么写 TO_DAYS() 分区才不踩坑?
核心是把日期转为整数,且边界值必须对齐自然月起始日。错误写法如 LESS THAN (TO_DAYS('2024-01')) 会报错;正确写法必须用完整日期:
- 用
TO_DAYS('2024-02-01')表示“2024 年 1 月及之前”,不是'2024-01-31' - 避免
UNIX_TIMESTAMP():跨时区或夏令时切换时结果不一致,TO_DAYS()更稳定 - 初始至少覆盖未来 3 个月:比如今天是 2026-06-09,
p_future分区的LESS THAN MAXVALUE必须存在,否则插入 2026-07 数据会报ERROR 1526 (HY000): Table has no partition for value - 主键必须是联合主键:例如
PRIMARY KEY (id, create_time),单列PRIMARY KEY (id)会失败
简短示例:
CREATE TABLE order_history (
id BIGINT NOT NULL,
create_time DATE NOT NULL,
amount DECIMAL(10,2),
PRIMARY KEY (id, create_time)
) PARTITION BY RANGE (TO_DAYS(create_time)) (
PARTITION p202604 VALUES LESS THAN (TO_DAYS('2026-05-01')),
PARTITION p202605 VALUES LESS THAN (TO_DAYS('2026-6-01')),
PARTITION p_future VALUES LESS THAN MAXVALUE
);查历史数据加 ORDER BY 和 LIMIT 为啥反而变慢?
因为 MySQL 需要先从所有匹配分区中各自取出部分结果,再合并、排序、截断。例如 ORDER BY create_time DESC LIMIT 20,若涉及 12 个分区,就得各取至少 20 行,再全局排序——IO 和内存开销陡增。
- 解决方案一:缩小分区范围,让 WHERE 条件更精确,比如
WHERE create_time BETWEEN '2026-04-01' AND '2026-05-31' - 解决方案二:改用分区名显式指定,如
SELECT * FROM order_history PARTITION (p202604, p202605) ORDER BY create_time DESC LIMIT 20 - 注意:即使用了
PARTITION()语法,如果ORDER BY字段不是分区键或没索引,仍可能触发文件排序(Using filesort)
归档旧数据别直接 DROP PARTITION
DROP PARTITION 是 DDL 操作,快是快,但不可逆、无备份、不审计。生产环境应走迁移路径:
- 先导出旧分区数据:
SELECT * INTO OUTFILE '/tmp/p202401.sql' FROM order_history PARTITION (p202401) - 再清空分区:
TRUNCATE PARTITION p202401(比DROP安全,保留结构) - 或者用
REORGANIZE PARTITION合并多个旧分区,减少总数(MySQL 8.0+ 支持ALGORITHM=INPLACE) - 定期检查分区数量:超过 100 个会影响元数据性能,
SHOW CREATE TABLE查看当前定义
最易被忽略的一点:分区表不是查询加速银弹。如果业务里 80% 的查询都不带时间条件,或者总要 JOIN 另一张非分区表,那分区反而增加维护负担——先用 EXPLAIN PARTITIONS 看真实剪枝效果,再决定是否落地。


















