直接alter table add index会锁表,因mysql 5.6前需重建整表并全程持有mdl写锁,阻塞所有dml;5.6+虽支持inplace,但仍需短暂mdl_write锁且特定场景(如唯一索引、外键)会退化为copy模式。

为什么直接 ALTER TABLE ADD INDEX 会锁表?
MySQL 5.6 之前,ALTER TABLE ADD INDEX 会重建整张表,期间阻塞所有 DML(INSERT/UPDATE/DELETE),在生产大表上等于停机。5.6+ 虽支持 ALGORITHM=INPLACE,但并非所有情况都真正“无锁”——比如对非空字段加普通二级索引时,仍需获取 MDL_WRITE 锁等待全表 DML 结束,且构建索引过程本身会持有 SWRITE 锁,阻塞后续写入。
用 pt-online-schema-change 安全加索引的要点
Percona Toolkit 的 pt-online-schema-change 是目前最稳妥的方案,它通过创建影子表、触发器同步增量、原子重命名完成切换。但必须注意:
- 目标表必须有主键或唯一非空索引,否则无法可靠同步变更
- 确保
binlog_format = ROW,STATEMENT 模式下触发器可能丢失部分更新 - 执行前先用
--dry-run和--print验证语句逻辑,避免误删原表 - 监控
Threads_running和从库延迟,长事务或高并发写入时建议错峰操作 - 不要在从库上直接运行,它只应在主库执行;从库会自动同步重命名操作
MySQL 8.0+ 可考虑 INSTANT ADD COLUMN 但不适用于索引
MySQL 8.0 引入了 ALGORITHM=INSTANT,但它仅支持添加列(ADD COLUMN)且列不能含默认值或为 NULL(除非 innodb_strict_mode=OFF)。加索引依然走 INPLACE 流程,不支持 INSTANT。所以看到文档里写了 INSTANT,别误以为能秒加索引——ADD INDEX 永远不会是 INSTANT。
如果只能用原生命令,怎么最小化影响?
当无法引入外部工具时,可手动控制节奏:
- 先用
SHOW CREATE TABLE确认存储引擎是 InnoDB,且 MySQL ≥ 5.6 - 显式指定
ALGORITHM=INPLACE, LOCK=NONE,例如:ALTER TABLE orders ADD INDEX idx_user_id (user_id) ALGORITHM=INPLACE, LOCK=NONE; - 若报错
LOCK=NONE is not supported,说明该操作仍需锁表(如给 TEXT 列建前缀索引、或表有外键),此时必须换方案 - 用
SELECT SLEEP(1)配合SHOW PROCESSLIST观察 DDL 执行时长和阻塞状态,避免在业务高峰发起
真正难的不是语法,而是判断“这个索引到底要不要现在加”。很多线上慢查其实源于查询没走索引,而不是索引缺失——先确认 EXPLAIN 输出、检查 handler_read_* 状态变量,再决定是否动表结构。











