delete 不宜用于大表,因其逐行删除并记录完整日志(如 binlog/wal),触发索引更新、外键检查和触发器,导致日志膨胀、恢复缓慢;建议仅用于小数据量场景。

DELETE 会写满日志,别在大表上直接用
DELETE 是逐行删除,每删一行都记一条 DELETE 日志(比如 MySQL 的 binlog 或 PostgreSQL 的 WAL),还会触发索引更新、外键检查、触发器。这意味着:日志体积 ≈ 行数 × 平均行大小 + 索引开销,恢复时间也线性增长。
实操建议:
- 只用于小数据量(
WHERE匹配结果 - 加
WHERE条件前先EXPLAIN确认走了索引,否则可能全表扫描+锁表 - MySQL 中若用
innodb_file_per_table=OFF,DELETE 后空间不释放,得跟OPTIMIZE TABLE - PostgreSQL 中大量 DELETE 容易导致 bloat,后续需
VACUUM,不是“删完就轻了”
TRUNCATE 几乎不写行级日志,但不能带条件
TRUNCATE 是 DDL 操作,它重置表的存储结构(如清空数据页、重置 auto-increment 计数器),不走事务日志中的行变更记录,所以快、日志少、空间立即回收。但它不支持 WHERE,也不能在有外键引用的表上直接执行(MySQL 8.0+ 允许,但 PostgreSQL 仍拒绝)。
实操建议:
- 适合清空整张表,且业务能接受 ID 重置、触发器不触发、权限要求是
DROP级别(不是DELETE) - MySQL 中
TRUNCATE会隐式提交,无法回滚;PostgreSQL 中虽在事务内可回滚,但仍是 DDL,会影响并发 DDL 锁 - 注意某些云数据库(如 AWS RDS for MySQL)对
TRUNCATE有额外复制延迟风险,尤其开启 GTID 时
DROP PARTITION 只影响指定分区,但仅限分区表
如果表按时间/范围/列表做了分区(如 PARTITION BY RANGE (created_at)),DROP PARTITION 是最干净的“批量删除”方式:它直接删掉整个分区的数据文件,不写行日志,不触碰其他分区,也不影响全局索引(除非是全局分区索引)。
实操建议:
- 必须提前建好分区策略,且目标数据集中在少数几个分区里(比如删掉
p_202301和p_202302) - MySQL 中执行后,分区定义从
INFORMATION_SCHEMA.PARTITIONS消失,但要注意ALTER TABLE ... DROP PARTITION会重建表元数据,短时锁表 - PostgreSQL 使用
DETACH PARTITION+DROP TABLE组合实现类似效果,更显式但步骤多一步 - 误删分区不可逆——没有回收站,备份没覆盖到该分区就真没了
日志量排序和真实性能陷阱
按日志写入量从小到大: DROP PARTITION ≈ TRUNCATE ≪ DELETE。但性能不能只看日志——TRUNCATE 在大表上可能卡住几秒等锁,DROP PARTITION 要求你早就把分区建对了,而 DELETE 看似慢,却最容易在凌晨低峰期“偷偷跑完”。
最容易被忽略的点:
- MySQL 5.7 默认 binlog_format=STATEMENT 时,
TRUNCATE不写 binlog,主从可能不一致;必须设为MIXED或ROW - 所有操作在从库重放时行为可能不同——比如
DROP PARTITION在从库找不到对应分区会报错中断复制 - 哪怕只是删 1% 的数据,如果它分散在全表各页,
DELETE的随机 IO 和锁竞争可能比 TRUNCATE 整表还伤











