MySQL大表加字段锁表主因是InnoDB默认用COPY算法重建整表;仅当满足NULL/DEFAULT、非主键等全部条件时才启用INSTANT(秒级),否则降级为INPLACE(短锁)或COPY(长锁)。

MySQL大表加字段锁表,不是配置没调好,而是InnoDB默认可能走COPY算法——只要不满足INSTANT全部条件,就会重建整张表,锁住MDL写锁直到拷完。
怎么判断你的ADD COLUMN会不会锁死业务?
别猜,直接用EXPLAIN FORMAT=TREE看执行计划。在ALTER TABLE前加这个前缀:
EXPLAIN FORMAT=TREE ALTER TABLE t ADD COLUMN c VARCHAR(32) DEFAULT '';
重点看两处输出:
- 若含
"alter_algorithm": "instant"→ 几乎零耗时,放心执行 - 若含
"access_type": "table_scan"→ 即使写了ALGORITHM=INPLACE,也可能退化为全表扫描+重写索引页,I/O卡住就等于锁延长
另外,执行前务必查长事务:SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW()) - TIME_TO_SEC(TRX_STARTED) > 60; —— 有运行超1分钟的事务,DDL就得等它提交,期间所有新请求排队。
ALGORITHM=INSTANT的硬性门槛有哪些?
MySQL 8.0.12+才支持INSTANT,但只对“纯元数据变更”生效,必须同时满足:
- 新增列定义带
DEFAULT值,或允许NULL(NOT NULL且无DEFAULT?直接COPY) - 不能是主键、唯一索引列、生成列、全文索引列
- 表引擎是
InnoDB,且innodb_file_per_table=ON(默认已开,但老实例可能关着) - 不能同时修改已有列(哪怕只是改个
COMMENT)
例如:ALTER TABLE t ADD COLUMN status TINYINT DEFAULT 0 ALGORITHM=INSTANT, LOCK=NONE; ✅
而ALTER TABLE t ADD COLUMN status TINYINT NOT NULL ALGORITHM=INSTANT; ❌(报错降级)
当INSTANT不适用,又不能停机,该选pt-online-schema-change还是gh-ost?
看你的瓶颈在哪:
- 如果主库写压力大、怕触发器拖慢(比如每秒几千INSERT),选
gh-ost:它不依赖触发器,靠解析binlog同步,要求binlog_format=ROW且binlog_row_image=FULL - 如果环境老旧(MySQL 5.5/5.6)、外键多、或DBA更熟悉Percona工具,用
pt-online-schema-change:注意它会在原表建三个触发器,高并发下可能引发锁竞争 - 别用
ALGORITHM=COPY硬扛:1000万行表在普通SSD上拷一次就要20秒以上,期间所有DML阻塞,连接池大概率打满
示例(gh-ost):
gh-ost --host=localhost --database=test --table=t --alter="ADD COLUMN c VARCHAR(32) DEFAULT ''" --chunk-size=1000 --max-load="Threads_running=25" --initially-drop-ghost-table --execute
最容易被忽略的细节:从库和连接池
在从库上直接跑ALTER?危险。GTID模式下,从库应用该DDL时同样会锁表,导致复制延迟突增,甚至中断;并行复制线程也可能因DDL串行化而堆积。
连接池呢?锁表期间新连接不断尝试获取MDL读锁,但被DDL的写锁拦住,最终耗尽连接数,应用报Lock wait timeout exceeded或Too many connections——这不是数据库崩了,是连接池先扛不住了。


















