23.postgresql delete 和truncate的差异
postgresql delete 和truncate的差异
在 PostgreSQL 中,TRUNCATE 比 DELETE 快得多。两者差异的核心在于操作机制和MVCC(多版本并发控制)的影响,具体原因如下
1. 操作本质不同
-
DELETE 是 DML(数据操作语言):逐行扫描表,将每条记录的 xmax 标记为当前事务 ID,生成死元组(dead tuple)。这些死元组在事务提交后依然占用物理空间,需要后续 VACUUM 清理。同时,每行删除操作都会产生对应的 WAL(预写日志)记录,用于崩溃恢复和复制。
-
TRUNCATE 是 DDL(数据定义语言):直接重新初始化表的存储文件(或删除并重建文件),不逐行处理。它仅修改表的元数据(如 pg_class 中的 relpages 和 reltuples),并将表所占用的磁盘块标记为可重用,不产生任何死元组。
2. 事务回滚的差异
-
DELETE 支持回滚:回滚时,PostgreSQL 需要将标记为删除的行恢复为可见状态(通过将事务状态改为 aborted),但死元组依然存在,只是事务状态变化。这一过程仍需依赖 MVCC 机制,且不会立即回收空间。
-
TRUNCATE 也支持回滚(PostgreSQL 8.4 起):它通过将表的存储文件重命名并记录到事务状态中来实现回滚,而非记录每一行的变更。回滚时,只需恢复原来的文件结构。但 TRUNCATE 不产生大量 WAL 日志(对于临时表甚至不记录 WAL),因此执行和回滚都非常快。
3. “不产生回滚数据”的说法修正
-
严格来说,TRUNCATE 仍然会产生少量回滚信息(如事务中记录了文件操作),但远少于 DELETE 逐行产生的数据。如果说“不产生 MVCC 死元组”或“不产生每行的 WAL 日志”,则更加准确。
-
许多资料强调 TRUNCATE 快是因为不需要写大量 WAL,并且无需后续 VACUUM 清理,而非“不会产生回滚数据”。
4. 性能对比总结
| 特性 | DELETE |
TRUNCATE |
|---|---|---|
| 操作类型 | DML | DDL |
| 处理方式 | 逐行标记删除 | 直接重置存储文件 |
| MVCC 影响 | 产生大量死元组 | 无死元组 |
| WAL 日志量 | 与删除行数成正比 | 极少(仅元数据变更) |
| 空间回收 | 需 VACUUM 异步回收 |
立即释放(需排它锁) |
| 事务回滚 | 慢(需恢复每行) | 快(回滚文件操作) |
结论
TRUNCATE 比 DELETE 快,是因为它避免了逐行删除带来的大量 WAL 写入、MVCC 死元组积累以及后续 VACUUM 的开销,而非简单地“不产生回滚数据”。实际上,两者在事务中都可以回滚,但回滚成本截然不同。
mysql和ORACLE中RUNCATE 与 DELETE 的差异
在 MySQL 和 Oracle 中,TRUNCATE 和 DELETE 的行为差异很大,主要体现在事务处理机制和空间回收方式上。以下是核心区别的对比
一、TRUNCATE 与 DELETE 的核心差异
二、关键机制详解
1. 为什么 MySQL 的 TRUNCATE 更快且不可回滚?
- MySQL 的 TRUNCATE 实际上是 DROP + CREATE 的逻辑:先删除原表,再创建一个同结构的空表。
- 整个过程只记录极少的日志(仅元数据变更),不记录每行删除,因此速度极快。
- 因为是 DDL,会隐式提交事务,所以无法回滚
2. 为什么 Oracle 的 TRUNCATE 后空间可能没释放?
- Oracle 的 TRUNCATE 默认只重置高水位线(High Water Mark),让表看起来是空的,但数据块仍保留在表空间中供后续插入重用。
- 若想立即释放空间给其他段使用,需指定 DROP STORAGE 选项:
TRUNCATE TABLE table_name DROP STORAGE;
3. DELETE 的空间回收差异
- MySQL:DELETE 只标记行为已删除,空间不释放。要回收空间需执行:
OPTIMIZE TABLE table_name; -- 或 ALTER TABLE ... ENGINE=InnoDB
本质是重建表。
- Oracle:DELETE 后,空间由后续的 INSERT 重用,但物理文件大小不会缩小。若要收缩表空间,需使用 ALTER TABLE ... SHRINK SPACE(需要开启行移动)或导出再导入。
4. 事务与锁的哲学差异
- MySQL (InnoDB):默认自动提交,强调高并发写入,使用 MVCC + 间隙锁在 RR 级别下解决幻读,但也可能带来更多的锁等待。
- Oracle:默认不自动提交,强调读写不互斥,使用回滚段实现一致性读(写不阻塞读),但长事务可能导致 UNDO 表空间膨胀
三、使用建议
总结一句话:
- MySQL 的 TRUNCATE 是“物理删除 + 重建”,快但不可逆,适合彻底清空临时表。
- Oracle 的 TRUNCATE 是“逻辑清空 + 保留空间”,空间可复用,适合快速清空但后续还会大量插入的场景。
- DELETE 在两者中都支持回滚,但 MySQL 默认自动提交需手动开启事务,而 Oracle 事务隐式开启需显式提交。

浙公网安备 33010602011771号