MySQL锁表与否取决于ALGORITHM算法与操作类型:INPLACE支持多数索引及部分列变更(有短暂MDL锁),INSTANT仅限末尾增删列(零锁),COPY则全程锁表;需用EXPLAIN FORMAT=JSON预检确认是否真正支持在线。

ALTER TABLE锁表与否取决于ALGORITHM和操作类型
MySQL是否锁表,不是看版本新不新,而是看具体执行时选了哪种算法、改的是什么字段。5.6起支持ALGORITHM=INPLACE,但这个“支持”是条件性的:它只对部分变更生效,且失败时不会静默降级,而是直接报错或退回到COPY模式。比如ADD COLUMN加在末尾、不带NOT NULL DEFAULT值,通常走INPLACE;但只要加个DEFAULT 'abc',哪怕只是字符串,默认就会触发全表回填,变成COPY——整张表写锁数分钟到数小时。
-
INSTANT(8.0.12+):仅限末尾加列、删除列,真正零拷贝,不锁表 -
INPLACE:多数索引操作、部分列类型放宽(如VARCHAR(100) → VARCHAR(200))可用,但仍有短暂MDL_EXCLUSIVE锁 -
COPY:改列约束、加NOT NULL、重命名列(CHANGE COLUMN)、修改TEXT默认值等,必然全程锁表
Waiting for table metadata lock 的真实含义
看到这个状态,别急着KILL ALTER线程——它大概率还没拿到锁,真正在阻塞它的,是另一个早就在运行的会话。元数据锁(MDL)的冲突本质是“读写互斥”:SELECT拿MDL_SHARED_READ,UPDATE拿MDL_SHARED_WRITE,而ALTER TABLE必须拿到MDL_EXCLUSIVE。只要有一个未提交事务(哪怕只是SELECT * FROM t后忘了COMMIT),就会长期持有MDL_SHARED_READ,让ALTER无限等待。
- 查谁在等锁:
SELECT * FROM performance_schema.metadata_locks WHERE LOCK_STATUS = 'PENDING' - 查谁拿着锁:
SELECT * FROM performance_schema.metadata_locks WHERE LOCK_STATUS = 'GRANTED' - 查长事务:
SELECT trx_id, trx_started, trx_state, trx_query FROM information_schema.INNODB_TRX ORDER BY trx_started LIMIT 5
为什么EXPLAIN FORMAT=JSON比SHOW CREATE TABLE更可靠
文档里写的“支持INPLACE”,不等于你这条语句就真能走INPLACE。MySQL内部会根据表结构、字符集、存储格式、是否含BLOB等做动态判断。靠猜或查手册容易翻车。最稳妥的方式是先用EXPLAIN FORMAT=JSON预检:
MySQL 数据库管理技能,能够通过自然语言查询和管理数据库,支持 SELECT/INSERT/UPDATE/DELETE、表管理、备份恢复等操作。当用户提及 MySQL、数据库查询、建表或数据备份时使用此技能。
EXPLAIN FORMAT=JSON ALTER TABLE t ADD COLUMN x INT;
重点看返回里的"alter_algorithm": "inplace"和"supports_inplace": true两项。如果其中任一为false,就说明这次操作必然锁表,得换方案。
pt-online-schema-change不是万能解药
它确实能绕过MDL锁,但代价是引入新风险:触发器延迟、binlog堆积、主从不一致。尤其要注意几个硬限制:
- 原表必须有主键或唯一非空索引,否则无法分块同步
- 已有触发器的表不能用,会和pt-osc自建触发器冲突
- 禁止在从库单独运行再切主从——
pt-osc不复制DDL,会导致主从结构分裂 - 执行前务必加
--dry-run --print,确认生成的RENAME TABLE语句目标库名正确、顺序无误
真正难处理的,从来不是“怎么让ALTER不锁”,而是“怎么让业务能容忍一次安全的锁”。很多团队卡在这一步,不是技术不会,是没把长事务清理、连接池超时、监控告警这些配套环节补上。

















