根本原因是drop table需长时间持有lock_open等全局锁并清理buffer pool中散落的大量缓存页,导致opening tables等待、page_cleaner超时及服务挂起。

根本原因不是“删文件慢”,而是 DROP TABLE 在执行过程中会竞争并长时间持有多个全局锁,同时触发高开销的内存结构清理操作——尤其是当表曾被大量访问、缓存页散落在 Buffer Pool 各处时。
为什么 LOCK_open 会让所有查询挂起
整个 DROP TABLE 流程被 pthread_mutex_lock(&LOCK_open) 包裹。这个互斥锁控制着 MySQL 打开/关闭表的核心路径,一旦被占住,任何新连接尝试打开表(包括 SELECT、INSERT、甚至 SHOW TABLES)都会排队等待。
- 在 Ext3/Ext4 文件系统上删除一个 10GB 的
.ibd文件可能耗时数秒,期间LOCK_open一直不释放 - 即使使用 XFS,如果 Buffer Pool 扫描卡在 LRU 链表遍历上(比如缓存了 5 万页),同样会拖长持锁时间
-
SHOW PROCESSLIST里大量线程状态为Opening tables或Waiting for table metadata lock,就是它在作祟
Buffer Pool 扫描为何 CPU 低但 I/O 高
MySQL 5.7+ 的 DROP TABLE 不再同步刷脏页,但仍需从每个 Buffer Pool 实例的 LRU 和 flush list 中摘除该表所有 page。这不是顺序读,而是随机跳转查找——尤其 old sublist 上页面分布稀疏,链表遍历本身不耗 CPU,但伴随大量 mutex 竞争和 cache miss,导致内核态等待加剧。
- 查缓存页数量:
SELECT COUNT(*) FROM information_schema.INNODB_BUFFER_PAGE WHERE TABLE_NAME = 'db/tbl';返回 >1000 就值得警惕 - 看 InnoDB 状态:
SHOW ENGINE INNODB STATUS\G中若频繁出现buf_pool_mutex等待,基本可锁定问题 - 大规格实例(如 48c488G)更容易出问题,因为 Buffer Pool 大(>64GB)、LRU 链表更长、扫描耗时呈非线性增长
AHI 清理引发 page_cleaner 超时
删除表还要清理自适应哈希索引(AHI)。AHI 是基于 buffer pool page 构建的 hash 表,DROP 时需逐个定位并删除对应条目。这会与 page_cleaner 线程争抢 dict_operation_lock,导致后者 loop 延迟超 4 秒,错误日志中出现:
page_cleaner: 1000ms intended loop took 24915ms. (flushed=182 and evicted=0, during the time.)
- 这种延迟不是孤立事件,而是连锁反应:page_cleaner 卡住 → flush list 积压 → 新写入被迫等待 → 更多线程堆积在
LOCK_open -
SET GLOBAL innodb_adaptive_hash_index = OFF;可临时规避,但需评估对线上点查性能的影响 - MySQL 8.0 把
TRUNCATE改为DROP + CREATE,反而让部分场景又回归到全量 AHI 扫描逻辑
真正影响服务可用性的不是磁盘,是锁粒度
很多人盯着 .ibd 文件大小或磁盘 I/O,却忽略了一个事实:哪怕表只有 1MB,只要它曾在高峰期被高频访问过,缓存页分散在几十个 buffer pool instance 的 LRU 链表尾部,DROP 仍可能卡住 10 秒以上——而这 10 秒,足够让应用层连接池耗尽、超时雪崩。
- 外键未清理、长事务未结束、只读实例回放阻塞,这些都会放大锁等待效应
-
TRUNCATE TABLE在 5.7 中仍是同步清理 data page,性能未必比DROP好多少;而 8.0 的实现反而更重 - 最稳妥的“大表下线”方式,其实是先
RENAME TABLE t TO t_old,再异步DROP,把锁影响控制在毫秒级











