delete比truncate慢,核心原因是它逐行删除并写undo/redo日志、触发约束与触发器、维护高水位线,而truncate直接释放数据页且不记录单行操作。

DELETE 比 TRUNCATE 慢,核心原因不是“它慢”,而是它干了更多事:写 Undo、写 Redo、逐行触发、维护高水位线(HWM)——而 TRUNCATE 一条都不做。
DELETE 为什么必须写大量 Undo 和 Redo
DELETE 是 DML 操作,每删一行,Oracle 就得把这行的旧值拷进 Undo 表空间,以便后续 ROLLBACK;同时在 Redo 日志里记下“某块某行被删了”,保证实例崩溃后能恢复一致性。100 万行 = 100 万次 Undo 写 + 100 万次 Redo 记录。尤其当表有索引、外键或触发器时,Undo/Redo 体积还会翻倍。
常见错误现象:ORA-01555: snapshot too old 或事务执行中途卡住、UNDO_TABLESPACE 快满,基本都源于 DELETE 长时间持有 Undo 段。
- 小表(
- 大表无 WHERE 条件时,
DELETE FROM t实际等价于全表扫描 + 全行标记删除,I/O 和内存压力陡增 - 若未显式
COMMIT,Undo 段会一直被占用,影响其他长事务
TRUNCATE 完全绕过 Undo/Redo 行级记录
TRUNCATE 是 DDL,本质是“扔掉整个数据段,重建空结构”。它只在 Redo 中记一条“释放 Extent X–Y”的元数据操作,Undo 完全不参与。没有行级回滚能力,但换来的是毫秒级响应(哪怕上千万行)。
使用场景很明确:你确定要清空整张表,且不需要任何单行恢复能力,也不依赖触发器逻辑。
- 不能对视图、同义词或外部表执行
TRUNCATE TABLE,会报ORA-01702 - 即使加了
REUSE STORAGE,也依然不写行级日志,只是保留原分配空间不归还表空间 - PL/SQL 块中不能直接写
TRUNCATE TABLE t,必须用EXECUTE IMMEDIATE 'TRUNCATE TABLE t'
高水位线(HWM)差异直接影响后续查询性能
DELETE 不动 HWM,表块仍被标记为“曾被使用过”。下次 SELECT 全表扫描时,Oracle 仍要一路扫到原 HWM 位置,哪怕里面全是空块——这就是为什么删完数据后查询反而更慢。
TRUNCATE 则重置 HWM 到 INITIAL 大小,后续插入从头开始填块,物理读大幅减少。
- 可通过
ANALYZE TABLE t ESTIMATE STATISTICS或查询DBA_SEGMENTS.BLOCKS与DBA_EXTENTS.BLOCKS对比验证 HWM 变化 - 如果误用 DELETE 清空大表又没及时
ALTER TABLE t SHRINK SPACE,等于埋了个性能雷 -
SHRINK SPACE虽能降 HWM,但它本身要加行级锁、需启用行移动,开销远超 TRUNCATE
权限与触发器行为差异常被忽略
TRUNCATE 要求 DROP ANY TABLE 或对象属主权限;DELETE 只需 DELETE ON schema.table。更重要的是:TRUNCATE 完全不触发 任何 BEFORE/AFTER DELETE 触发器 —— 这点在审计、同步或业务逻辑强依赖触发器的系统中极易出问题。
- 例如某表的删除审计日志靠
AFTER DELETE触发器写入,用 TRUNCATE 后就彻底静默,毫无痕迹 - 外键约束检查:DELETE 会逐行校验子表是否存在引用;TRUNCATE 直接报错(除非先禁用约束),不会“悄悄跳过”
- 分区表上,
TRUNCATE TABLE默认清空所有分区;若只想清某一分区,必须用TRUNCATE TABLE t DROP STORAGE PARTITION p1
真正容易被忽略的点不在速度本身,而在“DELETE 看似安全,实则留坑;TRUNCATE 看似激进,实则干净”。选哪个,取决于你是否需要回滚、是否依赖触发器、是否在意后续查询的物理扫描范围——而不是单纯看执行时间那零点几秒。











