TRUNCATE在MySQL 5.7中会卡住Buffer Pool,因其需复用space id而强制遍历所有Buffer Pool实例的LRU和flush list逐页摘除缓存页,反复持有buf_pool_mutex,导致其他线程阻塞在Opening tables或Waiting for table metadata lock状态。

TRUNCATE为什么会让Buffer Pool卡住
因为MySQL 5.7的TRUNCATE TABLE不是简单删文件,它要复用space id,所以必须从Buffer Pool所有实例的LRU链表和flush list中逐个摘除该表的缓存页。这个过程会反复持有buf_pool_mutex,其他线程一碰buffer pool就排队等锁——你看到的“卡”,其实是大量查询停在Opening tables或Waiting for table metadata lock状态。
5.7中替代TRUNCATE的实操方案
官方明确建议用DROP TABLE + CREATE TABLE代替TRUNCATE(见MySQL 5.7文档TRUNCATE TABLE章节)。原因很直接:DROP不强制扫描全buffer pool,只清理flush list里的脏页;CREATE是空表初始化,开销极小。实测同一张500万行、12GB的表:
-
TRUNCATE耗时47秒,期间iostat显示%util持续100%,await峰值超200ms -
DROP+CREATE共耗时3.2秒,IO无明显波动
注意两个前提:DROP需有DROP权限(不是DELETE权限),且表不能被外键引用(否则报错ERROR 1701 (HY000))。
执行前必须做的三件事
哪怕改用DROP + CREATE,也得先压低buffer pool压力,否则仍可能卡在AHI清理或dict锁上:
- 查该表当前缓存页数:
SELECT COUNT(*) FROM information_schema.INNODB_BUFFER_PAGE WHERE TABLE_NAME = 'db/tbl';—— 若 > 2000,说明LRU扫描压力真实存在 - 临时关AHI:
SET GLOBAL innodb_adaptive_hash_index = OFF;(执行完记得恢复,否则点查性能可能下降10%~30%) - 选业务低峰期操作,并提前用
SET GLOBAL innodb_fast_shutdown = 0;确保下次启动能彻底清理残留结构
容易被忽略的分区表陷阱
MySQL 5.7不支持TRUNCATE TABLE t PARTITION (p1),强行执行会报错ERROR 1064。但很多人误以为DROP PARTITION能绕过buffer pool问题——其实不行。ALTER TABLE ... DROP PARTITION内部仍走truncate逻辑,同样触发全buffer pool扫描。真要清冷分区,正确做法是:
- 先
RENAME TABLE t TO t_old, t_new TO t;把热表让出名字 - 对
t_old执行DROP TABLE(此时它已无业务访问,buffer pool页自然老化) - 一周后再删
t_old,基本无卡顿
真正难处理的从来不是命令本身,而是那张表在buffer pool里到底还留了多少页——没查过INNODB_BUFFER_PAGE就动手,等于蒙眼拆弹。


















