truncate几乎不卡住其他查询,因其瞬时获取表级排他锁,在毫秒级完成元数据操作,不扫描数据页、不逐行加锁;而delete需为每行加锁、写undo和binlog,易阻塞并发查询。

TRUNCATE为什么几乎不卡住其他查询
因为它的锁是瞬时表级锁,不是逐行加锁。执行TRUNCATE TABLE t时,MySQL确实要获取排他锁(X lock),但整个操作在毫秒级完成:释放 segment、重置 .ibd 文件头、清空 FSEG_HEADER——全部在内存和元数据层完成,不扫描任何数据页。所以你几乎看不到 SHOW PROCESSLIST 里出现 Waiting for table metadata lock 或 Updating 状态。
而 DELETE FROM t 即使没 WHERE,也会启动事务、遍历聚簇索引、为每一行加 X 锁、写 undo、更新二级索引位图。100 万行 = 100 万个锁申请/释放周期,锁持有时间线性增长,极易阻塞 SELECT ... FOR UPDATE 或其他 DML。
- 常见卡点:
innodb_log_file_size不足导致频繁刷盘,SHOW ENGINE INNODB STATUS显示log sequence number滞后 - 主从延迟飙升的典型诱因:binlog_format = ROW 时,100 万行 DELETE 生成百万级 event,从库 SQL 线程单线程回放扛不住
- 别信“TRUNCATE 不锁表”——它锁,只是太快,监控工具采样都抓不到
DELETE 的日志开销到底有多大
DELETE 是完整 DML 流水线:每行触发一次 MVCC 版本链维护、一次 undo log 写入、一次 binlog event 序列化(ROW 模式下含完整前镜像)、一次二级索引 delete bit 更新。这些不是“可选”,是 InnoDB 引擎强制行为。
实测对比(InnoDB,100 万行,innodb_file_per_table = ON):
-
DELETE FROM t:耗时约 18 秒,产生约 20MB redo + undo 日志,binlog 增长 15MB(ROW 模式) -
TRUNCATE TABLE t:耗时 0.01 秒,redo 仅记录一条 DDL 元数据变更,binlog 只有一条TRUNCATE TABLE t事件 - 磁盘空间不释放:DELETE 后
DATA_LENGTH不变,必须OPTIMIZE TABLE才能收缩 .ibd
TRUNCATE 失败的四个隐藏检查点
很多人以为语法对就能跑通,其实 TRUNCATE 成功的前提是四层环境同时就绪——漏一项,凌晨三点告警就来了。
-
FOREIGN_KEY_CHECKS = 1:报错Cannot truncate a table referenced in a foreign key constraint,需先SET FOREIGN_KEY_CHECKS = 0 - 权限不足:需要
DROP权限,不是DELETE权限;线上账号常被限制 - 活跃事务持有该表的 MDL 锁:哪怕只是个未提交的
SELECT ... FOR UPDATE,TRUNCATE 就会卡在Waiting for table metadata lock - 复制模式冲突:GTID 模式下,若从库启用了
enforce_gtid_consistency = ON,TRUNCATE 必须在事务内执行(但 TRUNCATE 自身隐式提交,实际不可行)
什么时候绝对不能用 TRUNCATE
TRUNCATE 是删表重建,不是清空数据。只要需求里带“部分”“条件”“保留自增 ID”“触发业务逻辑”,就必须用 DELETE。
- 要删三个月前的数据?→ 只能
DELETE FROM t WHERE create_time ,TRUNCATE 不支持 WHERE - 有 ON DELETE 触发器要做审计或同步?→ TRUNCATE 不触发任何 DELETE 触发器
- 下游依赖 AUTO_INCREMENT 当唯一标识?→ TRUNCATE 重置计数器,下次
INSERT从 1 开始,可能撞 ID - 想回滚?→
BEGIN; TRUNCATE TABLE t; ROLLBACK;中的 ROLLBACK 无效,TRUNCATE 隐式提交
真正容易被忽略的,是 TRUNCATE 的原子性代价:它快,是因为绕过了引擎;它危险,也是因为绕过了引擎——没有 undo,没有 binlog 行事件,没有后悔药。生产上清空大表前,先确认那张表是不是真的“全量无残留价值”。










