分区键必须是主键的一部分,否则建表报错error 1503;mysql强制要求所有唯一键(含主键)必须包含分区表达式中的全部列,这是语法级限制而非优化建议。

分区键必须是主键的一部分,否则建表直接报错
MySQL 强制要求:只要表有主键或唯一索引,分区键就必须包含在其中。这不是优化建议,是语法级限制。ERROR 1503 (HY000): A PRIMARY KEY must include all columns in the table's partitioning function 这个错误出现,基本就是主键没带上时间字段。
常见错误写法:PRIMARY KEY (id) + PARTITION BY RANGE COLUMNS(create_time) → 必报错。
正确做法只有两种:
-
PRIMARY KEY (id, create_time)(推荐,保留自增语义,且满足约束) -
PRIMARY KEY (create_time, id)(适合时间强顺序插入场景,但可能影响INSERT性能)
别想着用 UNIQUE KEY (create_time) 替代主键——它不满足“主键含分区键”要求,仍会失败。
RANGE COLUMNS 分区比 YEAR()/TO_DAYS() 更安全稳定
用 YEAR(create_time) 或 TO_DAYS(create_time) 做分区表达式,看似简单,实则埋雷:
-
TO_DAYS()对输入格式极度敏感,'2024-01-01 '(尾部空格)或时区偏差都会导致建表失败 -
YEAR()无法处理跨年查询边界,比如WHERE create_time BETWEEN '2024-12-28' AND '2025-01-03'可能触发多个分区扫描 - MySQL 官方文档明确建议:对
DATE/DATETIME列优先使用RANGE COLUMNS
正确示例:
CREATE TABLE archive_log (
id BIGINT AUTO_INCREMENT,
user_id INT,
create_time DATETIME NOT NULL,
content TEXT,
PRIMARY KEY (id, create_time)
) PARTITION BY RANGE COLUMNS(create_time) (
PARTITION p202401 VALUES LESS THAN ('2024-02-01'),
PARTITION p202402 VALUES LESS THAN ('2024-03-01'),
PARTITION p202412 VALUES LESS THAN ('2025-01-01'),
PARTITION p_future VALUES LESS THAN (MAXVALUE)
);
注意:LESS THAN 的边界值必须是字符串字面量(如 '2024-02-01'),且严格按日期对齐,否则分区裁剪会失效。
本地索引(LOCAL)才是归档表的默认选择
归档表绝大多数查询都带时间范围条件,比如 WHERE create_time >= '2024-06-01'。这种场景下,LOCAL 索引天然匹配分区结构,每个分区维护自己的 B+ 树,查询时只需访问目标分区的索引页,IO 和内存开销最小。
而 GLOBAL 索引是跨分区的单一大索引,一旦涉及 DROP PARTITION,就必须重建整个索引——这在归档操作中完全不可接受。
建索引时无需显式声明 LOCAL,只要分区表存在,CREATE INDEX 默认就是本地索引:
-
CREATE INDEX idx_user_time ON archive_log (user_id, create_time)→ 自动 LOCAL - 若想加速
user_id单字段查询,仍需确保该索引包含create_time,否则分区裁剪失效
验证是否生效:执行 EXPLAIN PARTITIONS SELECT * FROM archive_log WHERE create_time >= '2024-06-01',看 partitions 列是否只列出几个分区名;如果显示 NULL 或全部分区,则说明裁剪失败,大概率是 WHERE 中用了函数包裹 create_time。
DROP PARTITION 是归档动作的核心,但磁盘空间不会立刻释放
归档的本质不是“查得快”,而是“删得快”。DROP PARTITION p2023_q1 是 DDL 操作,毫秒级完成,不走事务日志、不触发触发器、不维护二级索引——这是它碾压 DELETE FROM ... WHERE create_time 的根本原因。
但要注意两个现实约束:
- 执行后,对应
.ibd文件大小不变,InnoDB 不会自动收缩;必须后续执行OPTIMIZE TABLE archive_log或重建表才能回收磁盘空间 - 该分区一旦被 DROP,数据彻底不可逆;务必确认无任何业务还在查询这个时间范围,否则不是慢,是查不到
真正容易被忽略的是:分区裁剪是否真在起作用。很多团队建了分区却没效果,就是因为日常查询习惯性写 WHERE DATE(create_time) = '2024-01-01' ——函数一包,分区键就失效了,优化器只能全表扫所有分区。











