MySQL 5.7 对大表 ADD COLUMN 几乎必然触发全表重建并长时间锁表,因存储引擎限制与 MDL 机制共同作用;即使指定 ALGORITHM=INPLACE 或 LOCK=NONE,满足任一降级条件(如 NOT NULL DEFAULT、行格式变化等)即退化为 COPY 模式,导致所有 DML/SELECT 阻塞;实测千万级表加字段耗时数分钟至数十分钟;可靠方案仅限 pt-online-schema-change、主从切换或升级至 MySQL 8.0.12+ 使用 INSTANT DDL。

ALTER TABLE 在 MySQL 5.7 中对大表加字段,几乎必然触发全表重建(COPY 算法),不是“可能锁表”,而是“一定锁表且时间很长”——这是由存储引擎层限制和元数据锁(MDL)机制共同决定的。
ADD COLUMN 在 5.7 中默认走 COPY 算法
- 即使语句写了
ALGORITHM=INPLACE,只要满足以下任一条件,MySQL 就会悄悄降级为COPY:- 表非空且新增字段带
NOT NULL DEFAULT值(需回填每一行) - 字段类型变更导致行格式变化(如
varchar(255)→varchar(1024),长度头从 1 字节升为 2 字节) - 表使用
COMPACT行格式且含BLOB/TEXT字段
- 表非空且新增字段带
-
COPY模式下,MySQL 会:- 创建临时表(
#sql-ib*.ibd),结构含新字段 - 逐行拷贝原表数据(触发所有二级索引重建)
- 拷贝期间全程持有
MDL_WRITE锁 → 所有SELECT/INSERT/UPDATE/DELETE都阻塞
- 创建临时表(
- 实测千万级表加一个
VARCHAR字段,锁表时间常达数分钟至数十分钟,取决于磁盘 IO 和 CPU。
长事务会让 MDL_WRITE 锁无限期等待
-
ALTER TABLE必须获取MDL_WRITE锁,但只要有任意一个活跃事务(哪怕只是执行了SELECT * FROM t)没提交,它就只能排队等。 -
lock_wait_timeout默认是 31536000 秒(1 年),所以你看到的不是报错,而是“卡住不动”。 - 查证方式不是只看
SHOW PROCESSLIST,而要用:SELECT * FROM sys.schema_table_lock_waits\G
或更精准地联合查:SELECT t.trx_id, t.trx_started, p.ID, p.USER, p.HOST, p.INFO FROM information_schema.INNODB_TRX t JOIN information_schema.PROCESSLIST p ON t.trx_mysql_thread_id = p.ID WHERE p.COMMAND = 'Sleep' AND t.trx_state = 'RUNNING' AND t.trx_started < NOW() - INTERVAL 5 MINUTE;
LOCK=NONE 在 5.7 中形同虚设
- 很多人误以为加了
LOCK=NONE就真不锁表,但实际生效前提极苛刻:- 表不能有外键约束
- 不能有全文索引
- 不能有
GENERATED COLUMN - 不能是分区表
- 更关键:必须没有长事务、没有未提交的读事务(哪怕只读)
- 即使全部满足,
ADD COLUMN在 5.7 中也仅对无默认值的 NULL 字段支持LOCK=NONE;一旦带DEFAULT,立刻退化为LOCK=SHARED或更糟。
真正能落地的方案只有三个:用 pt-online-schema-change 做影子表迁移、提前在从库操作再主从切换、或升级到 MySQL 8.0.12+ 用 INSTANT DDL。
别信“加个参数就能在线”的说法——5.7 的底层能力就摆在那里,绕不开。


















