不能直接删主键再建复合主键,因MySQL要求AUTO_INCREMENT列必须包含在新主键中且位于最右;正确做法是单条语句ALTER TABLE t DROP PRIMARY KEY, ADD PRIMARY KEY (tenant_id, id),或用表重建法规避锁表与隐式DB_ROW_ID问题。

不能直接“删主键再建复合主键”而不锁表、不重建、不踩坑——尤其当原主键含 AUTO_INCREMENT 时,MySQL 会直接报错或隐式阻塞。
为什么 ALTER TABLE DROP PRIMARY KEY 后不能立刻加复合主键?
MySQL(特别是 5.7 和多数 8.0.x 生产版本)要求:如果原主键列是 AUTO_INCREMENT,你必须在新主键中包含它,且它必须放在组合列的最右位置。否则执行 ALTER TABLE ADD PRIMARY KEY (tenant_id, id) 会触发 ERROR 1075 (42000): auto increment column must be defined as a key。
常见错误现象:
- 执行
ALTER TABLE t DROP PRIMARY KEY后,表看似“没主键”,但 InnoDB 立即生成隐藏DB_ROW_ID,后续加主键若数据有重复,直接失败 - 想把单列
id主键换成(tenant_id, id),却漏掉给id先加索引,导致加主键失败 - 用
pt-online-schema-change尝试删含自增的主键,工具直接拒绝:Cannot drop primary key that contains an AUTO_INCREMENT column
正确重建复合主键的三步实操路径
适用于原主键为 id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,目标是 (tenant_id, id):
- 先确保
tenant_id列存在、类型匹配(如VARCHAR(32) NOT NULL),且无 NULL 值;否则加主键时会因隐式转换或约束失败 - 执行
ALTER TABLE t ADD INDEX idx_tenant_id (tenant_id)(可选但推荐,加速后续操作) - 一步完成删除旧主键 + 添加新主键:
ALTER TABLE t DROP PRIMARY KEY, ADD PRIMARY KEY (tenant_id, id)—— 注意顺序不能颠倒,且必须在同一语句中,否则中间状态会触发隐藏主键
这条语句在 MySQL 8.0.13+ 中可能免重建(仅当满足“原主键唯一、无外键、无其他唯一约束”等严苛条件),但线上环境请默认按全表重建处理:大表需评估锁表时间。
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
大数据量表(>100万行)的避坑替代方案
避免长事务锁表,用表重建法更可控:
- 创建结构一致的新表:
CREATE TABLE t_new LIKE t - 修改新表结构:
ALTER TABLE t_new DROP PRIMARY KEY, ADD PRIMARY KEY (tenant_id, id) - 迁移数据(注意只插业务字段,让
id自增重排):INSERT INTO t_new (tenant_id, col1, col2, ...) SELECT tenant_id, col1, col2, ... FROM t - 原子切换:
RENAME TABLE t TO t_old, t_new TO t
该方式绕过所有 ALTER 的锁和重建限制,但要注意外键、触发器、应用连接缓存需同步处理;INSERT 过程中务必关掉 FOREIGN_KEY_CHECKS 和 UNIQUE_CHECKS 加速。
删主键后忘记重建的隐性代价
很多人删完主键就以为“只是去掉约束”,实际影响远超预期:
- InnoDB 自动生成的隐藏
DB_ROW_ID不参与任何查询优化,EXPLAIN显示type: ALL成常态 - binlog event 在备库回放时无法定位行,靠全表扫描匹配,复制延迟飙升
- 二级索引叶子节点不再附带主键值,导致相同前缀的索引页分裂更频繁
- 后续想加回主键,若历史数据中
tenant_id和id组合已重复,ADD PRIMARY KEY直接报错退出,只能人工去重
真正关键的不是“怎么删”,而是删之前确认:这张表是否真能接受没有显式主键?哪怕只是临时状态。










