ALGORITHM=INPLACE仅表示尝试原地修改,并不保证不锁表;其实际是否锁表取决于操作类型、表结构(如外键、触发器)、innodb_file_per_table配置及事务状态,且始终需获取元数据锁(MDL)。

ALGORITHM=INPLACE 不等于不锁表
显式写 ALGORITHM=INPLACE 只是告诉 MySQL 尝试原地修改,但是否真能避免锁表,取决于操作类型和当前环境。比如添加二级索引支持 ALGORITHM=INPLACE,但添加主键、修改列类型、加全文索引等仍强制走 ALGORITHM=COPY——后者必然拷贝全表,全程持有排他锁。
常见误判点:
-
ALGORITHM=INPLACE成功执行 ≠ 零锁:它仍需在开始/结束阶段获取元数据锁(MDL),若此时有长事务未提交,ALTER TABLE会卡在Waiting for table metadata lock - 即使语句里写了
ALGORITHM=INPLACE,MySQL 在检测到外键、触发器、或innodb_file_per_table=OFF时,会悄悄降级为COPY,且不报错,只在EXPLAIN FORMAT=JSON的输出里暴露"alter_algorithm": "copy" -
SHOW VARIABLES LIKE 'innodb_file_per_table'必须为ON,否则INPLACE不可用
LOCK=NONE 失败的典型原因
LOCK=NONE 要求操作完全支持并发读写,但 MySQL 对它的支持很苛刻。添加唯一索引时,哪怕数据已去重,也可能因以下原因失败:
- 表上存在未提交的事务(哪怕只是个
SELECT ... FOR UPDATE) - 有慢查询正在扫描该表(如没加
LIMIT的SELECT *) - 列类型不兼容:比如对
TEXT列建唯一索引,或字段含前缀长度超限(VARCHAR(256)建唯一索引需指定(255)) - 从库上直接执行:GTID 模式下,
LOCK=NONE可能被拒绝,因为从库 SQL 线程无法保证回放顺序与主库一致
遇到 LOCK=NONE is not supported 报错,不要删掉参数重试,应先查 SHOW PROCESSLIST 清理阻塞源,再降级为 LOCK=SHARED(允许读,暂停写)。
为什么加唯一索引特别容易卡住
加普通索引只需构建 B+ 树,而加唯一索引必须做全量唯一性校验——MySQL 会遍历聚簇索引,逐行检查目标列值是否重复。这个过程不锁表,但会:
- 持续占用 buffer pool,挤占业务查询缓存空间
- 产生大量随机 I/O,拖慢磁盘响应,间接导致
SELECT变慢、INSERT延迟升高 - 一旦发现重复值,立刻报错中断,错误信息如
ERROR 1062 (23000): Duplicate entry 'xxx' for key 'uk_col',但不指出具体哪几行
所以加唯一索引前,必须手动查重:SELECT col, COUNT(*) FROM t GROUP BY col HAVING COUNT(*) > 1;清理时注意 NULL 值在 InnoDB 中允许多个,但业务逻辑可能不允许。
大表加索引时真正危险的不是 DDL 本身
DDL 语句执行中那几秒的 MDL 锁其实不可怕,真正压垮数据库的是资源争抢:
- I/O 压力:索引构建期间,磁盘吞吐可能打满,连带影响其他表的查询和刷脏页
- CPU 占用:排序索引项、计算哈希、校验唯一性都是 CPU 密集型,可能让
mysqld进程 CPU 使用率飙到 100% - 内存抖动:大表扫描会频繁淘汰 buffer pool 中的热页,后续业务查询被迫走磁盘,延迟跳升
- 从库延迟:即使主库用了
LOCK=NONE,从库 SQL 线程单线程回放,一个耗时 20 分钟的 DDL 在从库可能跑 2 小时
所以别只盯着“锁不锁表”,得看 iostat -x 1 和 top。如果磁盘 %util 持续 >90% 或 CPU sys% 异常高,说明已经影响业务,该暂停或切到低峰期。


















