DELETE 是什么
DELETE 按条件删除行,逐行记录日志、可回滚、会触发 DELETE 触发器与级联外键动作。MySQL 单表 DELETE 支持 ORDER BY 与 LIMIT,多表语法不支持 LIMIT;PostgreSQL 用 USING 或子查询删除关联表的数据。大表删除必须分批做:一次性删掉上千万行会长时间持锁、产生大量 binlog 或 WAL,主从延迟直线上升。清空整表用 TRUNCATE 更快,但它不逐行记日志、不可回滚,还会重置自增值。
基本信息
| 作用 | 按条件删除行,可回滚,会触发触发器与级联动作 |
|---|---|
| MySQL 分批写法 | DELETE FROM t WHERE … ORDER BY id LIMIT 1000 |
| PostgreSQL 关联删除 | WHERE id IN (SELECT …) 或 USING 子句 |
| 清空整表 | TRUNCATE 更快,但不逐行记日志、不可回滚、重置自增值 |
| 空间回收 | 删除行不会立刻归还磁盘,需要后续重建表或碎片整理 |
| 生产替代方案 | 常用软删除标记 is_deleted 代替物理删除 |
典型使用场景
- 清理历史数据:按时间分批删除过期日志与流水
- 撤销操作产生的冗余行:按业务主键精确删除
- 数据修正:删除导入错误造成的重复行
- 归档前置动作:先复制到归档表,再删除原表数据
- 清理孤儿数据:删除没有对应主表的明细行
- 测试环境重置:清空业务表,注意 TRUNCATE 与自增值的关系
分批删除与关联删除
分批删除与关联删除
-- MySQL:分批删除,避免长事务与主从延迟
DELETE FROM logs
WHERE created_at < '2023-01-01'
ORDER BY id
LIMIT 1000; # 循环执行直到影响行数为 0
-- PostgreSQL:用子查询删除关联表数据
DELETE FROM order_items
WHERE order_id IN (
SELECT id FROM orders WHERE status = 'cancelled'
);
-- 清空整表(不可回滚,谨慎使用)
TRUNCATE TABLE tmp_import;删除方式对照
| 方式 | 能否回滚 | 空间回收 | 适用场景 |
|---|---|---|---|
| DELETE 带条件 | 可以 | 不立即归还磁盘 | 按条件清理,可分批 |
| DELETE 全表 | 可以 | 不立即归还磁盘 | 小表,或需要触发触发器 |
| TRUNCATE | 不可以 | 立即释放数据页 | 整表清空且无外键引用 |
| DROP 后重建 | 不可以 | 释放最彻底 | 归档完成后重建表 |
| 软删除标记 | 不涉及 | 不涉及 | 生产环境常规做法 |
DELETE 不带 WHERE 会清空全表:执行前先跑同条件的 SELECT COUNT(*),并在事务内执行确认,这是最低成本的事故拦截。
被删除的行空间不会立刻归还操作系统(InnoDB 常态),表空间回收要靠后续重建表或碎片整理,长期高频删除的表要定期维护。
分批删除的判断条件是「影响行数小于批大小」:当一批删不满时就说明已经删完,可以停止循环,避免空转。
PostgreSQL 的 DELETE 会留下死元组,需要 VACUUM 回收;大批量删除后建议检查 autovacuum 是否及时跟上,否则表和索引会持续膨胀。
分批删除要点
- 每批较小:一批几百到几千行,避免长事务与锁堆积
- 按主键或时间序推进:WHERE id > 上次位置,条件稳定且能走索引
- 批间留间隔:给主从复制与后台任务留出追赶时间
- 删除前先归档:把数据复制到归档表或冷存储再删
- 监控主从延迟:延迟超过阈值就暂停,避免影响读库
- 校验结果:删完对比剩余行数与业务预期是否一致
删除大表数据前先确认有可用的备份或归档:DELETE 可回滚的前提是事务尚未提交,一旦提交,恢复只能依赖备份或日志。
分批删除要和主从延迟监控一起上线:只分批不看延迟,遇到业务高峰期依然可能把从库压垮,暂停与恢复的条件要提前定好。