mysql不支持对已有非分区表直接用alter table ... partition by添加分区,必须通过建新表→迁数据→切换的路径实现。

直接说结论:MySQL 不支持对已有非分区表直接用 ALTER TABLE ... PARTITION BY 添加分区(5.7 及之前版本会报错;8.0 虽部分支持,但有严苛前提,实际不可靠)。必须走「建新表 → 迁数据 → 切换」的路径。跳过这一步,后面所有操作都可能失败。
为什么不能直接 ALTER TABLE ... PARTITION BY?
ALTER TABLE ... PARTITION BY?- MySQL 5.7 及更早版本执行该语句会明确报错:
ERROR 1503 (HY000): A PRIMARY KEY must include all columns in the table's partitioning function - 即使在 MySQL 8.0+,若原表主键不含分区键(如按
date_push分区,但主键只是id),ALTER TABLE仍会失败 - 根本原因:分区键必须是主键或唯一索引的组成部分,而现有表结构通常不满足这一约束
常见错误现象:
- 执行
ALTER TABLE t1 PARTITION BY RANGE (YEAR(create_time)) (...)后卡住或报错 - 手动删掉原主键再加联合主键,结果触发外键/应用层主键冲突
- 误以为“加了索引就能分区”,但索引 ≠ 主键组成部分,无效
正确做法:建新分区表 + 数据迁移
核心步骤不是“改”,而是“换”——用一张结构合规的新表替代旧表。
实操要点:
- 新表
CREATE TABLE必须显式声明联合主键,把分区键包含进去,例如:PRIMARY KEY (id, date_push)(若按date_push分区) - 分区定义必须匹配业务节奏,比如时间字段用
COLUMNS(date_push)(支持 datetime 精确到秒)比YEAR(date_push)更稳妥,避免跨年边界误差 - 迁移用
INSERT INTO new_table SELECT * FROM old_table,别用mysqldump再导入——后者无法保证顺序、丢失自增值、且极慢 - 迁移前务必校验行数:
(SELECT COUNT(*) FROM old_table)和(SELECT COUNT(*) FROM new_table)必须一致
示例关键片段:
CREATE TABLE t_send_message_send_new (
id BIGINT NOT NULL AUTO_INCREMENT,
date_push DATETIME NOT NULL,
-- 其他字段...
PRIMARY KEY (id, date_push), -- 注意:联合主键含分区键
KEY idx_date_push (date_push)
) ENGINE=InnoDB
PARTITION BY RANGE COLUMNS(date_push) (
PARTITION p2024 VALUES LESS THAN ('2025-01-01'),
PARTITION p2025 VALUES LESS THAN ('2026-01-01'),
PARTITION p2026 VALUES LESS THAN ('2027-01-01')
);
切换时最容易被忽略的三件事
切表不是 rename 一下就完事,以下三点漏掉任一,都会导致线上故障:
-
应用连接未重连:rename 后,如果应用还持着旧表名的预编译语句(prepare statement)或连接池缓存,可能继续往已删除的旧表写入,报错
Table doesn't exist - 外键与触发器未同步处理:原表若有外键引用或触发器,新表不会自动继承,需手动重建;否则插入时违反约束或逻辑缺失
-
分区边界未预留未来值:比如只建到
p2026,但 2026 年 12 月的数据会因无对应分区而写入失败,报错ERROR 1526 (HY000): Table has no partition for value。务必加PARTITION p_future VALUES LESS THAN MAXVALUE
真正耗时的是数据迁移,不是语法。别在 ALTER TABLE 上反复试错,先确认主键结构和分区键是否兼容,再动手建新表——这是唯一稳定路径。











