mysql 5.7及之前drop primary key会全表重建并独占锁表,不可中断;8.0.13+仅在特定条件下免重建;主键变更需严格满足列顺序、类型、索引等约束,且须全面评估外键、应用、缓存影响。

ALTER TABLE ... DROP PRIMARY KEY 会锁表且不可中断
MySQL 5.7 及之前版本中,DROP PRIMARY KEY 必须同时指定新主键(或先加 ADD PRIMARY KEY),否则报错 ERROR 1075 (42000): Incorrect table definition; there can be only one auto increment column and it must be defined as a key。更关键的是:这个操作会触发全表重建(即使只是删主键),期间表被独占锁住,写入完全阻塞。
- MyISAM 引擎下是表级锁,整个过程无法并发写入
- InnoDB 下虽支持行锁,但删主键仍需重建聚簇索引,本质是 copy-and-replace 流程,耗时取决于数据量
- 线上大表(>10GB)执行可能持续数分钟甚至小时,务必避开业务高峰
- 8.0.13+ 支持
ALTER TABLE ... DROP PRIMARY KEY不重建(仅当主键是自增列且无其他唯一约束时),但多数生产环境仍是 5.7/8.0.28 之前的版本,不能依赖
用 ADD PRIMARY KEY 替换旧主键必须显式指定列顺序和类型
想把主键从 id 换成 (tenant_id, id)?不能只写 ADD PRIMARY KEY (tenant_id, id) 就完事。InnoDB 要求新主键列必须包含原 AUTO_INCREMENT 列(如果存在),且该列必须在组合主键的最右位置——否则会报 ERROR 1075 (42000): ... auto increment column must be defined as a key。
- 正确写法:
ALTER TABLE orders ADD PRIMARY KEY (tenant_id, id),前提是id是AUTO_INCREMENT且已设为KEY - 如果原
id是自增但没建索引,得先ADD INDEX idx_id (id),再执行主键变更 - 列类型必须严格一致:比如原
id INT UNSIGNED,新主键里也得是INT UNSIGNED,否则隐式转换失败 - 组合主键会让所有二级索引的叶子节点都带上这两个字段,显著增大索引体积,尤其
tenant_id值重复率高时,B+ 树层级可能变深
pt-online-schema-change 不是万能的,对主键变更有限制
pt-online-schema-change 能避免长事务锁表,但它在处理主键变更时有硬性限制:不能删除原主键列,也不能改变原主键列的 AUTO_INCREMENT 属性。一旦你试图用它删掉含自增的主键,工具会直接拒绝执行,并提示 Cannot drop primary key that contains an AUTO_INCREMENT column。
- 可行路径:先用
pt-osc添加新唯一索引 + 新字段,再用普通ALTER切换主键(仍需短时锁表) - 若原主键不含自增(如 UUID 字段),
pt-osc可以完成完整替换,但要求目标主键列已有NOT NULL和唯一性约束 - 注意
--chunk-index参数:默认用主键分块,主键变更中必须手动指定另一个唯一索引,否则同步会卡住 - 迁移后记得检查
information_schema.STATISTICS,确认新主键是否真正生效,有些情况下pt-osc成功但未刷新元数据缓存
重建主键前必须确认外键依赖和应用层缓存
主键不是孤立存在的。一旦修改,所有引用它的外键约束、ORM 的主键映射、Redis 里的 ID 缓存、前端传参逻辑都可能出问题。最隐蔽的坑是:某些应用把主键当成业务 ID 直接展示或拼接 URL,换主键后链接全部 404。
- 执行前跑
SELECT CONSTRAINT_NAME, TABLE_NAME, COLUMN_NAME FROM information_schema.KEY_COLUMN_USAGE WHERE REFERENCED_TABLE_NAME = 'your_table'查外键依赖 - 检查 ORM 配置(如 Django 的
primary_key=True字段、Rails 的id: false设置)是否与新结构匹配 - Redis 缓存键若含
user:<id></id>这类格式,主键变复合后必须同步改生成逻辑,否则缓存穿透 - 备份不只是 mysqldump:确保 binlog position 记录准确,万一回滚要用
mysqlbinlog找到变更前的最后快照











