是,mysql 5.6前add index全程锁表;5.6起仅innodb表且无外键、无全文索引、非主键/唯一索引时,才可能通过algorithm=inplace, lock=none实现免锁。

ALTER TABLE ADD INDEX 会锁表吗
在 MySQL 5.6 之前,ALTER TABLE ... ADD INDEX 会全程锁表(copy table 方式),DML 阻塞严重。5.6 起引入 ALGORITHM=INPLACE 和 LOCK=NONE 控制能力,但不是所有情况都支持无锁——联合索引添加是否真正无锁,取决于存储引擎、MySQL 版本、索引字段类型和是否含全文/空间索引等。
对 InnoDB 表,只要满足以下条件,ADD INDEX 可做到 DML 不阻塞(即 LOCK=NONE):
- MySQL ≥ 5.6.17(推荐 ≥ 5.7.5 或 8.0+,稳定性更高)
- 索引字段不含
TEXT/BLOB(或其前缀长度未超限制) - 不涉及主键重建(仅
ADD INDEX,非ADD PRIMARY KEY) - 表未启用
innodb_file_per_table=OFF(老版本下可能退化为 copy)
怎么确认 ALTER 是否真走 INPLACE 且 LOCK=NONE
执行前加 EXPLAIN FORMAT=JSON 或直接看 ALTER 的执行反馈。更可靠的是在执行时开启 INFORMATION_SCHEMA.INNODB_TRX 和 INFORMATION_SCHEMA.PROCESSLIST 监控:
运行 ALTER TABLE t ADD INDEX idx_a_b (a,b); 后,立即查:
SELECT trx_id, trx_state, trx_operation_state, trx_query FROM INFORMATION_SCHEMA.INNODB_TRX WHERE trx_query LIKE '%ADD INDEX%';
若 trx_operation_state 显示 adding index 且持续时间长,说明走的是 inplace;若很快结束又没报错,再查 PROCESSLIST 中是否有大量 Waiting for table metadata lock,有则说明被阻塞——大概率是隐式锁升级或 DDL 正在排队。
关键验证点:执行期间能否正常 INSERT/UPDATE/DELETE? 如果可以,基本确认是 LOCK=NONE;如果写入卡住几秒甚至更久,说明触发了短暂的 metadata lock 等待(常见于高并发下 DDL 与 DML 竞争 MDL)。
大表加联合索引的实操建议
即使支持 LOCK=NONE,大表建索引仍可能引发 I/O 压力、buffer pool 挤出、从库延迟等问题。不能只盯着“是否锁表”,还要控节奏:
- 避开业务高峰,尤其注意从库复制延迟——DDL 在主库秒完成,但从库可能需数分钟重放索引构建逻辑
- 用
ALTER TABLE ... ALGORITHM=INPLACE, LOCK=NONE显式声明(MySQL 5.6.21+ 支持),避免因优化器误判降级为 copy - 联合索引字段顺序很重要:把区分度高、常用于
=查询的列放前面,IN或范围查询列放后面;顺序错了,索引可能完全失效 - 建索引期间观察
SHOW ENGINE INNODB STATUS\G中的ROW OPERATIONS区块,若index create进度停滞,可能是磁盘 I/O 或内存不足 - 如表超 100GB 且无法停写,考虑分批建索引:先建单列索引验证效果,再合并;或用
pt-online-schema-change(但要注意其自身也占资源、有 binlog 放大风险)
容易被忽略的坑:NULL 值与联合索引最左匹配
联合索引 idx_a_b (a,b) 对 WHERE b = ? 完全无效——这是最常被误踩的点。另外,如果 a 列允许 NULL,而查询是 WHERE a IS NULL AND b = 1,InnoDB 仍能使用该索引(因为 NULL 被当作一个确定值存入 B+ 树),但 WHERE a 1 这类非等值判断大概率跳过索引。
还有个隐蔽问题:字符集不同导致隐式转换。比如 a 是 utf8mb4_general_ci,而查询条件传入的是 utf8mb4_0900_as_cs 字符串,MySQL 可能放弃使用索引。检查执行计划时务必确认 key_len 和 type(应为 ref 或 range,而非 ALL)。
真正麻烦的从来不是“能不能加”,而是“加完有没有被用上”——上线前必须用真实慢查 SQL 跑 EXPLAIN 验证。











