MySQL 5.6+ ADD INDEX 卡住主因是未满足LOCK=NONE条件:非InnoDB引擎、存在外键或FULLTEXT索引、版本过低、添加唯一索引等,导致校验失败报错而非降级;必须显式指定ALGORITHM=INPLACE, LOCK=NONE并提前检查表结构与运行环境。

MySQL 5.6+ 的 ADD INDEX 默认不锁表,但必须显式指定 ALGORITHM=INPLACE, LOCK=NONE,否则可能静默退化为锁表模式。
为什么 ALTER TABLE ADD INDEX 还是卡住了?
不是所有 ADD INDEX 都能真正 LOCK=NONE。MySQL 会校验条件,失败时直接报错,不会自动降级——所以看到语句卡在 altering table 状态,大概率是触发了隐式锁表。
- 表引擎不是
InnoDB:MyISAM、Memory 等不支持 Online DDL - 存在外键约束:
SHOW CREATE TABLE t1查看输出里是否有FOREIGN KEY - 有
FULLTEXT或SPATIAL索引:这类索引添加强制ALGORITHM=COPY - MySQL 版本低于 5.6.7:旧版本即使写
LOCK=NONE也会忽略 - 目标列是主键或唯一约束的一部分:
ADD UNIQUE INDEX不支持LOCK=NONE
ALGORITHM=INPLACE 和 LOCK=NONE 到底怎么配?
ALGORITHM 控制“怎么做”,LOCK 控制“锁多狠”,两者必须协同。只写一个没用,不写则按默认策略(通常是 ALGORITHM=DEFAULT, LOCK=DEFAULT),而默认值因版本和操作类型而异,不可控。
- 安全写法必须同时指定:
ALTER TABLE t1 ADD INDEX idx_col1 (col1) ALGORITHM=INPLACE, LOCK=NONE; -
ALGORITHM=INPLACE表示复用原表空间,不建临时表;若失败会报ALGORITHM=INPLACE not supported -
LOCK=NONE是目标,但 MySQL 会反向校验:如果发现无法满足(比如有外键),就拒绝执行,而非悄悄切到LOCK=SHARED - 别依赖
ALGORITHM=DEFAULT:它可能选COPY,尤其在加主键或改列类型时
实在不敢赌内置 Online DDL 怎么办?
当表结构复杂、版本老旧、或业务不允许任何风险时,pt-online-schema-change 是更稳的兜底方案——它不依赖 MySQL 内置 DDL 机制,而是用触发器 + 影子表模拟在线变更。
- 命令示例:
pt-online-schema-change --alter "ADD INDEX idx_col2 (col2)" D=test,t=t1 --execute - 前提条件必须满足:表有主键或唯一非空索引;
binlog_format=ROW;从库无延迟 - 它全程不锁原表,但会占用双倍磁盘空间、增加主从延迟、对写入吞吐有 10%~30% 影响
- 务必先跑
--dry-run和--print看执行计划,线上跳过这步等于裸奔
真正容易被忽略的三件事
语法写对只是第一步。实际执行前,磁盘空间、buffer pool 竞争、长事务阻塞元数据锁,这三个点比参数配置更容易导致失败或雪崩。
-
df -h /var/lib/mysql必查:即使ALGORITHM=INPLACE也要临时排序缓冲,空间不足直接中断 -
SELECT * FROM information_schema.INNODB_TRX ORDER BY TRX_STARTED;查长事务:未提交事务会阻塞 MDL 锁获取,让ALTER卡死在“Waiting for table metadata lock” -
SHOW PROCESSLIST看有没有大查询正在扫全表:高并发读会加剧 buffer pool 压力,建索引过程可能拖慢整体 QPS


















