mysql加索引锁表是执行模型所致,非配置问题;需查长事务和sleep连接释放mdl锁;algorithm=inplace+lock=none有前提且不零影响;pt-osc是生产首选,但要求主键且禁在从库单独运行。

大表重建索引在 MySQL 5.7 及更早版本中几乎必然锁表数分钟到数小时,这不是配置没调好,而是执行模型决定的——它默认走全表 COPY 流程,全程持有 MDL_EXCLUSIVE 锁,业务读写全部阻塞。
为什么 ALTER TABLE ... ADD INDEX 会卡在 Waiting for table metadata lock
这不是索引构建慢,是元数据锁(MDL)被其他连接占着不放。哪怕一个未提交的 SELECT * FROM t,也会持续持有 MDL_SHARED_READ;而加索引需要 MDL_EXCLUSIVE,二者互斥。你看到的“卡住”,其实是 ALTER 在等那个 Sleep 连接释放锁。
- 用
SHOW PROCESSLIST查状态为Waiting for table metadata lock的线程,同时留意Command = Sleep且Time > 300的连接 - 用
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW() - trx_started) > 60找真正在跑的长事务 -
SHOW OPEN TABLES WHERE In_use > 0对 MDL 无效,别依赖它
ALGORITHM=INPLACE, LOCK=NONE 真的不锁表吗
能显著降低锁级别,但不等于“零影响”。MySQL 会按规则判断是否可降级,失败就报错,不会静默退化。加二级索引时它大概率走 inplace,但有硬性前提:
- 表必须使用
innodb_file_per_table = ON(否则直接 fallback 到 COPY) - 不能有外键、全文索引、触发器或长事务;否则
LOCK=NONE会被拒绝或自动降级为LOCK=SHARED - 执行前务必用
EXPLAIN FORMAT=JSON确认输出里有"alter_algorithm": "inplace"和"lock": "none" - 即使
LOCK=NONE成功,I/O 和 CPU 压力仍会导致查询变慢——这不是锁,是资源争抢
pt-online-schema-change 是生产环境首选方案
它绕开 MDL 锁本身:新建影子表 → 用触发器捕获原表变更 → 分块拷贝历史数据 → 最后原子切换表名。整个过程原表读写基本不受影响。
- 要求原表必须有主键或唯一非空索引,否则无法做增量同步;已有触发器的表不能用
- 执行前加
--dry-run --print看它生成的 SQL,尤其注意RENAME TABLE语句顺序和目标库名是否正确 - 禁止在从库单独运行再切主从——
pt-osc不复制 DDL,会导致主从表结构不一致 - 真实难点不在工具本身,而在触发器生效期间应用是否容忍短暂延迟(比如重复写入、主键冲突)
最常被忽略的一点:即使你用了 ALGORITHM=INPLACE,只要表上有任何未提交事务,或者存在显式 LOCK TABLES,MDL 就卡死。所以“先查长事务”不是可选项,是必做动作。











