不能直接TRUNCATE TABLE,除非确认表完全空闲、无长事务、无外键依赖且能接受元数据锁阻塞;其卡住主因是需等待MDL锁,任意未提交SELECT都会阻塞它并连锁影响所有查询。

不能直接 TRUNCATE TABLE,除非你已确认表完全空闲、无长事务、无外键依赖,且能接受几秒到几分钟的元数据锁阻塞。 千万级表上 TRUNCATE 卡住,根本原因不是“数据多”,而是它必须抢到 MDL(元数据锁)——只要有一个未提交的 SELECT 或隐式事务正在读这张表,TRUNCATE 就会卡在 Waiting for table metadata lock,连带阻塞所有后续查询。
为什么 TRUNCATE 在大表上也会卡住
TRUNCATE TABLE 是 DDL 操作,在 InnoDB 中实际触发:等待所有活跃事务释放该表的 MDL 锁、清空 buffer pool 中相关页、重置 AUTO_INCREMENT、标记 .ibd 文件为可回收。其中第一步(等锁)最不可控。
- 常见现象:
SHOW PROCESSLIST里看到状态长期为Waiting for table metadata lock - 影响范围:不只是写操作,任何未提交的
SELECT(包括SELECT ... FOR UPDATE或 autocommit=0 下的普通查询)都会阻塞它 - 分区表例外:若按时间分区,优先用
ALTER TABLE t DROP PARTITION p202401,毫秒级,不锁表
安全替代方案:RENAME + DROP(金蝉脱壳法)
核心是把“删数据”变成“换名字”,把耗时操作异步化,业务几乎无感。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- 第一步(秒级):
RENAME TABLE big_log TO big_log_to_drop—— 原表瞬间不可见,新表名立即可用 - 第二步(可选):
CREATE TABLE big_log LIKE big_log_to_drop,或让应用重建空表 - 第三步(后台):
DROP TABLE big_log_to_drop—— 此时才真正清理数据,IO 压力由后台 purge 线程慢慢消化 - 关键检查点:
RENAME后立刻翻应用日志,确认无Table doesn't exist报错;若有,说明存在硬编码表名,需改代码或加兼容视图
删完磁盘空间还不释放?这是正常现象
InnoDB 的 .ibd 文件不会因 DROP 自动缩小,空间只是标记为“可复用”。刚删掉 50GB 数据,du -sh 看文件大小不变是预期行为。
- 验证是否真释放:
SELECT FILE_NAME, TABLE_NAME, ALLOCATED_SIZE FROM INFORMATION_SCHEMA.INNODB_TABLESPACES(MySQL 8.0+),看ALLOCATED_SIZE是否下降 - 想物理收缩?只能导出再导入:
mysqldump --no-create-info big_db big_table | mysql big_db,但千万级表 dump/load 成本极高,慎用 - 更现实做法:监控
innodb_data_free(通过SHOW ENGINE INNODB STATUS),只要它持续增长,说明空间已在内部复用
最容易被忽略的一点:RENAME 前必须确认表没被任何存储过程、事件、触发器引用——这些对象不会随表名变更自动更新,运行时会直接报错。

















