truncate 直接释放 segment 并重置 hwm,delete 仅标记行删除且不释放空间;truncate 是 ddl,不写 undo/redo 行日志,delete 是 dml,需记录每行变更。

TRUNCATE 直接释放 segment,DELETE 只标记行删除
TRUNCATE 是 DDL 操作,本质是丢弃原数据段(segment),再重建一个空的;它修改数据字典和区(extent)位图,把整段已分配的 extents 标记为“未使用”,立刻归还给表空间。哪怕表原来占了 100 个 extent,TRUNCATE 后 DBA_EXTENTS 中只剩 1 个(取决于 MINEXTENTS 设置)。而 DELETE 是 DML,只在每个数据块里打“已删除”标记,块本身仍归属该表,DBA_SEGMENTS.BYTES 和 DBA_EXTENTS.COUNT 完全不变——空间一毛没少。
高水位线(HWM)是否重置,决定后续物理读范围
HWM 是 Oracle 全表扫描的边界:哪怕块里全是空行,只要在 HWM 下,就得读。DELETE 不动 HWM,清空后执行 SELECT * FROM t 仍要扫到原高位,I/O 毫无改善;TRUNCATE 则直接把 HWM 重置到 INITIAL 大小,后续插入从头填块,物理读大幅减少。验证方式很简单:ANALYZE TABLE t ESTIMATE STATISTICS 后查 DBA_SEGMENTS.BLOCKS 与 DBA_EXTENTS.BLOCKS 差值,就能看出 HWM 是否回落。
TRUNCATE 不写 undo/redo 行级日志,DELETE 必须写
DELETE 每删一行,就要往 undo 表空间存旧值、往 redo 写“某块某行被删”,100 万行 = 100 万次 undo + 100 万次 redo 记录;还可能触发约束检查、索引更新、触发器执行。TRUNCATE 只在 redo 里记一条“释放 Extent X–Y”的元数据操作,undo 完全不参与。这也是它毫秒级完成、且不会引发 ORA-01555: snapshot too old 的根本原因——它根本不留回滚能力,换来了空间释放的彻底性。
REUSE STORAGE 子句不是“不释放”,而是“不归还”
很多人误以为加 REUSE STORAGE 就能让 TRUNCATE 像 DELETE 一样不释放空间。其实不然:TRUNCATE TABLE t REUSE STORAGE 仍会清空所有数据、重置 HWM、跳过触发器、隐式提交,只是不把 extents 归还给表空间——它们仍归属该表,但状态变为“未使用”。这意味着:空间没被其他对象复用,但后续 INSERT 依然从头开始填块,不会触发行迁移或浪费空块扫描。真正“不释放”的只有 DELETE;TRUNCATE 即便加这个子句,也比 DELETE 彻底得多。
最容易被忽略的一点是:TRUNCATE 的空间释放效果,依赖于底层段管理方式(如 ASSM 还是 MANUAL),且对 IOT、分区表、含 LOB 列的表有额外行为。直接看 DBA_SEGMENTS 和 DBA_EXTENTS 才算数,别光信 COUNT(*) 返回 0。











