MySQL 5.7 中 CHANGE COLUMN 必然锁表,因需全量重建;可用 pt-online-schema-change 将锁降至毫秒级,但需主键/唯一索引等前提,并注意外键、视图、存储过程及磁盘空间等兼容性问题。

MySQL 5.7 中无法真正“无锁”修改列名,ALTER TABLE ... CHANGE 必然触发表重建和元数据锁(MDL),但可通过策略把锁表时间压缩到业务可接受范围,同时避免旧代码崩掉——关键不是消灭锁,而是控制锁的时长和影响面。
为什么 CHANGE COLUMN 一定会锁表
5.7 不支持 RENAME COLUMN,CHANGE 本质是「删旧列 + 建新列 + 拷贝全量数据」,InnoDB 必须走 ALGORITHM=COPY(即使只改名)。哪怕一张 10 万行的表,也会卡住所有 DML 几秒;千万级表则可能达分钟级。这不是配置能绕开的限制,是引擎层行为。
- 执行时会持有
MDL_EXCLUSIVE锁,期间SELECT、INSERT、UPDATE全部排队等待 -
SHOW PROCESSLIST中状态显示为altering table,且Time字段持续增长 - 若中途被 kill,回滚过程本身也极慢,可能比执行还久
怎么让锁表时间缩到最低
核心思路:不改原表,用 pt-online-schema-change 替代原生命令,它把“锁”拆成毫秒级的行级拷贝+最终 0.1 秒 RENAME,业务几乎无感。
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
- 必须满足前提:
原表有主键或唯一非空索引、无触发器、操作用户有TRIGGER/REPLICATION SLAVE/PROCESS权限 - 命令模板:
pt-online-schema-change --alter "CHANGE old_col new_col VARCHAR(50) NOT NULL DEFAULT '' COMMENT '说明'" D=db,t=table_name --dry-run - 先跑
--dry-run:验证权限、外键、语法,失败信息比--execute清晰得多 - 线上必加参数:
--chunk-time=0.5(单次拷贝不超过 0.5 秒)、--max-lag=5(从库延迟超 5 秒自动暂停)
如何让旧业务代码继续跑通
字段名变了,但下游代码、视图、存储过程仍用旧名,直接报 Unknown column 'old_col' in 'field list'。这不是数据库问题,是依赖管理问题。
- 查所有依赖:
SELECT * FROM INFORMATION_SCHEMA.VIEWS WHERE VIEW_DEFINITION LIKE '%old_col%';,同理查INFORMATION_SCHEMA.ROUTINES和触发器 - 外键不会自动更新:先
SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE REFERENCED_COLUMN_NAME = 'old_col';,再手动DROP FOREIGN KEY+ADD FOREIGN KEY - 临时兼容方案(仅限灰度期):在应用层做字段映射,比如 ORM 配置里加别名
old_col → new_col,避免改 SQL - 严禁用
SELECT *:存量代码中若有此写法,字段位置一变就崩,必须定位并替换为显式列名
最容易被忽略的三个点
很多人跑通 pt-osc 就以为万事大吉,结果上线后第二天才发现问题:
-
pt-osc最后一步RENAME TABLE虽只耗毫秒,但仍是全局 MDL,若此时恰有长事务(如未提交的UPDATE),它会等那个事务结束才执行——锁等待可能被误判为“工具卡死” - 影子表会额外占用约 1.5 倍磁盘空间,
df -h空间不足时工具直接退出,错误提示却是 “Cannot chunk table”,容易误导 - 修改列名后,
information_schema.COLUMNS.COLUMN_NAME立即更新,但某些监控工具或 DBA 脚本若缓存了旧结构,可能持续报错数小时










