TRUNCATE TABLE最快且不引发主从延迟,因其是DDL操作,不写行级binlog、不生成undo日志、仅重置元数据;但不可回滚、不触发外键约束和触发器,有外键引用时直接报错。

直接用 TRUNCATE TABLE 是最安全、最快的方式,但前提是你能接受它不可回滚、不走事务、且不触发外键检查的特性;否则必须分批 DELETE,且不能靠 LIMIT + OFFSET。
为什么 TRUNCATE TABLE 能避免主从延迟
它不是“删数据”,而是重置表结构元数据:释放 .ibd 文件、清空 FSEG_HEADER、跳过 undo log 和 binlog(ROW 格式下完全不记录),从库无需回放任何行变更。但注意:TRUNCATE 需要 DROP 权限,且在 MySQL 5.7+ 的 ROW 格式下不写 binlog —— 这正是它不拖慢从库的根本原因。
常见误判点:
-
TRUNCATE在 STATEMENT 格式 binlog 中只记一条语句,看似安全,但若从库 SQL Thread 执行失败(如权限不足、表名冲突),会导致复制中断,比DELETE更难定位 - 有外键引用该表时,
TRUNCATE直接报错,而DELETE可能因约束检查失败卡在某一行 - MySQL 8.0 以下不支持
TRUNCATE TABLE t PARTITION (p1),分区表清空只能退化为逐个TRUNCATE或改用DROP PARTITION
DELETE 分批删除必须绕开的三个执行陷阱
很多人以为加了 LIMIT 就安全,实际反而埋雷。
- 禁用
DELETE FROM t WHERE ... ORDER BY id LIMIT 1000 OFFSET 10000:每次扫描都从头遍历,OFFSET 越大越慢;并发写入时id范围漂移,导致漏删或重复删 - 必须用确定性范围条件:例如
WHERE id BETWEEN 10000 AND 20000,且id字段必须有索引;没走索引就等于全表扫,每批都卡 - 别忽略从库压力:每批
COMMIT后建议SLEEP(0.05)(非强制但强推),否则瞬时大量小事务打满从库 IO 和锁队列,Seconds_Behind_Master会阶梯式跳升
从库端不调这两个参数,主库拆得再细也没用
主库把一个大事务拆成 100 个小事务,如果从库还是单线程回放,照样卡在最后一步。
-
slave_parallel_type = LOGICAL_CLOCK:启用基于组提交的并行复制,要求主库binlog_transaction_dependency_tracking = COMMIT_ORDER(MySQL 5.7.22+ 默认) -
slave_parallel_workers = 4(或更高,视从库 CPU 核数而定):worker 数太少起不到并行效果;但超过 8 个后收益递减,还可能因调度开销反降吞吐
验证是否生效:SHOW PROCESSLIST 中应看到多个 Worker 线程处于 Waiting for an event from Coordinator 或 Executing event 状态,而非只有一个 SQL Thread 长期 Updating。
删完数据,磁盘空间为什么还不释放
这是最容易被忽略的收尾动作。InnoDB 删除后空间只是“标记可复用”,不会返还给操作系统。
- 独立表空间(
innodb_file_per_table = ON,默认):删表或清空后,.ibd文件仍占磁盘;唯一彻底释放方式是重启 MySQL 实例,触发后台 purge 线程归还空间 - 系统表空间(
ibdata1):删表根本不会释放空间,必须重建整个实例才能回收 —— 这也是为什么生产环境必须开innodb_file_per_table -
OPTIMIZE TABLE对已清空的表无效,它只对仍有数据但存在碎片的表有用;对空表执行只会重建一次空文件,徒增 IO


















