delete不释放空间是因oracle仅标记删除、不降hwm;需先启row movement,再shrink space降hwm,最后手动resize数据文件才能真正释放磁盘空间。
delete 后表空间不释放,根本不是权限问题
删完数据发现 dba_segments.bytes 没变、dba_free_space 里空闲空间没涨——这不是因为你没 dba 权限,也不是因为没授权 resize 权限。oracle 的设计逻辑就是:delete 只标记行删除,不回收块,更不移动高水位线(hwm)。哪怕你用 sys 用户执行,结果也一样。
常见错误现象包括:
- 刚删掉 90% 行,
SELECT COUNT(*)很快,但SELECT *全表扫描依然极慢 -
USER_TABLES.BLOCKS和删之前完全一致,NUM_ROWS却大幅下降 -
EMPTY_BLOCKS值突然变大,说明 HWM 下方堆着大量“空但被锁定”的块
确认 HWM 是否虚高:必须查 BLOCKS 而非 BYTES
别只盯着 DBA_SEGMENTS.BYTES——它反映的是段(segment)已分配的总字节数,跟有没有数据无关。真实水位得看块级统计:
先确保统计准确:EXEC DBMS_STATS.GATHER_TABLE_STATS('SCHEMA_NAME', 'TABLE_NAME')
再查关键字段:SELECT BLOCKS, EMPTY_BLOCKS, NUM_ROWS FROM USER_TABLES WHERE TABLE_NAME = 'YOUR_TABLE'
判断依据:
- 如果
NUM_ROWS是几千,BLOCKS却是几十万,基本可断定 HWM 虚高 -
BLOCKS - EMPTY_BLOCKS才是当前真正有数据的块数,乘以 8192 可估算实际占用字节 -
EMPTY_BLOCKS为负?说明 SHRINK 后 HWM 已压到初始 extent 之下,属正常现象
SHRINK SPACE 必须开 ROW MOVEMENT,否则报 ORA-10636
ALTER TABLE ... SHRINK SPACE 不是开关式命令,它依赖底层行物理迁移能力。没启用行移动,Oracle 连第一块都挪不动:
必须按顺序执行:
ALTER TABLE your_table ENABLE ROW MOVEMENT-
ALTER TABLE your_table SHRINK SPACE COMPACT(整理碎片,加 RX 锁,影响小) -
ALTER TABLE your_table SHRINK SPACE(真正降 HWM,第二阶段需短暂 X 锁) - 完成后建议:
ALTER TABLE your_table DISABLE ROW MOVEMENT,避免干扰基于ROWID的应用逻辑
注意限制:
- 表不能有函数索引、域索引或位图连接索引(shrink 后可能失效)
- 不能在并行 DML 会话中执行
- Flashback Data Archive 启用时直接报错,不可用
收缩完数据文件仍不缩容?RESIZE 是手动活,Oracle 从不自动切文件
HWM 降了,只是段内空间回收了;.dbf 文件尺寸没变,操作系统就看不到空间释放。这一步常被跳过,且必须人工干预:
先确认数据文件末尾是否有连续空闲空间:
SELECT file_name,
bytes/1024/1024 AS curr_mb,
(bytes - NVL((SELECT SUM(bytes) FROM dba_free_space WHERE file_id = d.file_id), 0))/1024/1024 AS used_mb
FROM dba_data_files d
WHERE tablespace_name = 'YOUR_TS'
只有当 curr_mb - used_mb > 100(单位 MB),才值得执行 RESIZE:
ALTER DATABASE DATAFILE '/path/to/your.dbf' RESIZE 5000M
关键提醒:
-
RESIZE只能缩小到“当前 HWM 实际覆盖的最高块位置 + 1 个 extent”,不能无脑设成 1G - 若文件末尾被某个小对象占着(比如一个 8K 的 IOT 索引),
RESIZE会报ORA-03297,得先MOVE或DROP那个对象 - 生产环境务必在低峰期操作,
RESIZE是独占锁,阻塞所有对该文件的写入











