mysql大表加字段锁表主因是innodb默认用copy算法重建整表;仅当新增列满足null/default、非主键、引擎为innodb且启用innodb_file_per_table等全部条件时才启用instant(秒级),否则降级为inplace(短锁)或copy(长锁)。

MySQL大表加字段锁表,不是配置没调好,而是InnoDB默认走COPY算法重建整张表——数据量越大,拷贝越久,排他MDL锁就握得越紧。
为什么ADD COLUMN会触发COPY算法
根本原因在于:MySQL只对满足全部条件的操作才启用ALGORITHM=INSTANT(秒级),否则降级为INPLACE(短锁)或直接fallback到COPY(长锁)。触发COPY的典型场景包括:
-
ALTER TABLE t ADD COLUMN c INT NOT NULL;—— 没给DEFAULT,必须逐行填0,只能拷全表 - 新增列是主键、唯一索引列、生成列或全文索引列
- 表引擎不是InnoDB,或
innodb_file_per_table=OFF - 执行的是MySQL 5.7或更早版本(不支持INSTANT)
ALGORITHM=INPLACE也不等于零阻塞
很多人误以为加了ALGORITHM=INPLACE, LOCK=NONE就完全不锁,其实它分三阶段:
- 准备阶段:毫秒级
MDL写锁(需等当前所有读写事务释放锁) - 在线变更阶段:允许DML并发,但要重写聚集索引页和二级索引页,IO压力大时可能卡住
- 提交阶段:再抢一次毫秒级
MDL写锁
以下情况会让锁时间不可控:
- 表上有未提交的长事务:DDL必须等它释放
MDL读锁,期间所有新请求排队 - 磁盘IO瓶颈:SSD慢盘下,即使不拷全表,INPLACE仍可能因I/O卡顿
- 高并发写入:大量
INSERT/UPDATE导致在线变更阶段日志堆积,延长提交等待
如何验证当前操作是否真走INPLACE
别靠猜。执行前加EXPLAIN FORMAT=TREE看执行计划:
EXPLAIN FORMAT=TREE ALTER TABLE t ADD COLUMN c VARCHAR(32) DEFAULT 'x';
如果输出含"access_type": "table_scan",说明仍要扫全表,风险极高。
也可以在执行后查information_schema.INNODB_TRX确认是否有长时间持有的MDL锁。
当必须用COPY时,怎么把业务影响压到最低
硬跑ALGORITHM=COPY等于主动瘫痪服务。此时应绕过MySQL原生DDL锁机制:
- 用
pt-online-schema-change:适合MySQL 5.6–8.0,依赖触发器同步增量,但高并发写入下可能拖慢主库 - 用
gh-ost:推荐高负载场景,不依赖触发器,靠解析binlog同步,但要求binlog_format=ROW且binlog_row_image=FULL - 手动影子表:仅限低流量窗口,步骤是
CREATE TABLE new_t LIKE old_t→ALTER TABLE new_t ADD COLUMN ...→INSERT INTO new_t SELECT *, DEFAULT_VALUE FROM old_t→RENAME TABLE old_t TO old_t_bak, new_t TO old_t
真正危险的从来不是“能不能加字段”,而是没意识到MDL锁会等长事务、没验证执行路径、也没预留回滚窗口——这些细节比语法本身更容易让服务突然失联。











