flush tables with read lock卡在waiting for global read lock,本质是等待锁而非执行慢;它需等所有活跃事务释放mdl写锁、所有线程关闭表对象后,才能完成上全局读锁、清表缓存、上全局commit锁三步操作。

FLUSH TABLES WITH READ LOCK 为什么卡在 Waiting for global read lock
它不是“执行慢”,而是“在等锁”——FLUSH TABLES WITH READ LOCK 必须等所有活跃事务释放 MDL 写锁、所有线程关闭正在使用的表对象,才能完成三步操作:上全局读锁、清表缓存、上全局 COMMIT 锁。
常见卡住原因:
-
SHOW PROCESSLIST里看到状态为Waiting for global read lock,同时存在长时间运行的SELECT(比如带SLEEP()或大表 JOIN)、未提交事务、或持有LOCK TABLES的会话 - 有慢查询正占用某张表,导致
close_cached_tables阶段无法清理该表的TABLE_SHARE对象(ref_count > 0) - 使用了
LOCK TABLES t1 READ后没UNLOCK TABLES,后续FLUSH TABLES会被阻塞(哪怕只 flush 其他表) - 主库上有长事务(如
BEGIN; UPDATE ...;后迟迟不COMMIT),它持有的 MDL 写锁直接挡住 FTWRL 获取全局读锁
卡住时 SHOW PROCESSLIST 看到的典型状态组合
你真正要盯的是三类线程共存的局面:
- 一个或多个线程状态为
Executing/Sending data/User sleep,运行时间远长于其他线程(比如 >60s) - 一个线程状态为
Flushing tables或Waiting for global read lock,Info列显示FLUSH TABLES WITH READ LOCK - 大量新连接或 DML 线程状态为
Waiting for table flush,且Time持续上涨
注意:Waiting for table flush 不一定表示你在执行 FLUSH —— 它也可能是其他线程刚执行完 FLUSH 但还没 UNLOCK TABLES,或者失败后残留了旧版本表结构(old_version),导致后续所有访问该表的线程都得等。
为什么 kill 错线程会让问题更糟
别一看到 Waiting for table flush 就去 KILL 那些被堵住的 INSERT/UPDATE 线程。它们很可能只是在等锁,本身没持锁;而真正卡住 FTWRL 的,往往是那个还在跑 SELECT ... SLEEP(100) 的线程,或一个没提交的事务。
- 杀掉业务写入线程 → 触发 InnoDB 回滚 → 占用 IO 和 CPU,延长锁等待链
- 杀掉持有
LOCK TABLES的会话 → 若它已断开但未自动UNLOCK,MySQL 不会立即回收其表锁引用(尤其在低版本),残留状态仍阻塞 FTWRL - 杀掉
FLUSH TABLES WITH READ LOCK自身线程 → 可能留下未清理的全局读锁状态,部分版本中UNLOCK TABLES不会自动触发,需手动干预
FTWRL 在从库上卡住的特殊陷阱
在从库执行 FLUSH TABLES WITH READ LOCK 前,必须确认两件事:
-
Seconds_Behind_Master = 0:否则备份的是延迟数据,且锁期间 SQL 线程停摆,延迟会越拉越大 -
SHOW SLAVE STATUS\G中SQL_Thread_State是Slave has read all relay log或类似空闲态,而非Reading event from the relay log - 没有正在执行的
STOP SLAVE或START SLAVE操作 —— 这些内部也会申请 MDL 锁,与 FTWRL 冲突
最容易被忽略的一点:FTWRL 成功后,你必须立刻记录 SHOW MASTER STATUS(主库)或 SHOW SLAVE STATUS(从库)里的 binlog 文件名和位置,然后**马上**执行 UNLOCK TABLES。中间多等 10 秒,业务写请求就可能堆积成千上万,连接数打满,监控失联。











