先直接回答一个核心问题:如果是MyISAM引擎,那基本没跑。MyISAM引擎下执行ALTER TABLE,直接“反赌”——全表锁定,读写全部“让道”。锁表的时间,就是表的数据量乘以拷贝数据的速度。碰上大表,几分钟、几小时都算正常。
怎么识别?很简单,跑一句SQL看看引擎:
SHOW CREATE TABLE your_table_name; —— 如果看到ENGINE=MyISAM,那锁表的根因基本上就找到了。
找到了问题,怎么处理?要分情况对待:
- 紧急止血:如果业务还在线上,立刻停掉非关键的写入操作,防止雪球效应滚起来。
- 稳妥迁移:稳妥的办法是先
mysqldump -u root -p db table > backup.sql备份,再新建一张ENGINE=InnoDB的表导进去。慢是慢了点,但安全。 - 在线变更(推荐):更推荐的是使用
pt-online-schema-change这类工具。它通过触发器加影子表的机制,基本实现了无锁改表结构。前提是表必须有主键,且没有外键依赖。

InnoDB下ALTER TABLE还锁很久?检查是否触发了元数据锁等待
那如果你用的是InnoDB,还遇到锁很久的问题,那就不是引擎本身的问题了。InnoDB本身支持Online DDL,但某些操作,比如加索引、改列类型、或改主键,还是会需要短暂的排他元数据锁(MDL)。问题不是拷贝数据慢,而是卡在等这个MDL释放上。
要查是谁在占着MDL,可以看看:
SELECT * FROM performance_schema.metadata_locks WHERE OBJECT_SCHEMA = 'your_db' AND OBJECT_NAME = 'your_table';
- 重点关注
LOCK_STATUS = 'PENDING'的行,这代表有线程在排队等锁。 - 记下
OWNER_THREAD_ID,去performance_schema.threads里找对应的SQL和状态。 - 常见的阻塞源:有长事务一直没提交、有
SELECT ... FOR UPDATE操作挂在那、或者另一个ALTER TABLE正在跑。
为什么OPTIMIZE TABLE会让Lock_time飙升?
再来看看OPTIMIZE TABLE。很多人把它当日常维护,但这其实是个“大杀器”。 对InnoDB来说,OPTIMIZE TABLE本质上是ALTER TABLE ... FORCE,它会重建表、整理碎片。这时候,它不光锁表,还会触发大量的行锁和间隙锁,如果表上还有活跃的写入,冲突就会非常严重。
所以,千万别把它当日常保养。只有满足以下全部条件,才值得考虑跑一下:
- 用
SHOW TABLE STATUS LIKE 'table_name';查一下,表的DATA_FREE远大于实际数据。 - 做过大量的
DELETE操作,且没有其他机制回收空间(InnoDB本身不会自动释放空间给操作系统)。 - 确认当前没有长事务、没有高频的
UPDATE或INSERT。
一个更安全的替代方案是:ALTER TABLE t ENGINE=InnoDB;(显式重建表)再加ANALYZE TABLE t;(更新统计信息)。
如何避免ALTER/OPTIMIZE期间业务中断?
最后,谈谈怎么预防。核心原则就一句话:别让DDL(数据定义语言)和业务SQL抢同一把MDL锁。线上环境操作,必须避开业务高峰期,提前预判阻塞链。
- 执行前必查:跑一下
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW() - trx_started) > 60;—— 干掉那些运行超过1分钟的事务。 - 避开主从延迟高峰:用
SHOW SLA VE STATUS\G确认Seconds_Behind_Master接近0,再动手。 - 设个超时:用
SET lock_wait_timeout = 3;临时降低MDL等待阈值,让阻塞快速失败,而不是无限期等下去。 - 监控要跟上:关注
performance_schema.events_statements_current里,LOCK_TIME突增,且STATEMENT包含ALTER或OPTIMIZE的记录。
说实话,真正难处理的是那种“刚改完表,下游应用就报Lock wait timeout exceeded”的情况。这往往意味着业务代码里混入了DDL依赖,或者把OPTIMIZE当成了缓存清理来用。这种耦合问题,得从应用层彻底切开,单靠调参数是扛不住的。
