不能,resize只能缩小数据文件物理上限,前提是文件末尾全为空块即hwm之后无数据;若hwm居中则报ora-03297,需先用shrink space或move下压hwm再resize。
resize 命令真能缩小表空间文件吗?
不能直接收缩已用空间,resize 只能调小数据文件的「物理分配上限」,前提是文件末尾全是空块——也就是高水位线(hwm)之后没数据。一旦 hwm 卡在中间,resize 会报错 ora-03297: file contains used data beyond requested resize value。
常见错误现象:执行 ALTER DATABASE DATAFILE '/u01/oradata/db/users01.dbf' RESIZE 500M; 后报 ORA-03297,但 dba_data_files 显示文件大小确实是 1G。
- 先查 HWM 位置:
SELECT file_id, block_id + blocks - 1 AS last_used_block FROM dba_extents WHERE file_id = X ORDER BY block_id DESC FETCH FIRST 1 ROW ONLY; - HWM 对应的物理块号 × 块大小 = 实际已用上限,必须小于
RESIZE目标值才能成功 -
RESIZE不移动数据、不重组段,纯文件系统级截断,快但前提苛刻
怎么把 HWM 往下压?不是 TRUNCATE 那种暴力清空
需要在线重组织,核心是让 Oracle 把数据块从高地址往低地址挪,腾出文件末尾空间。最常用的是 ALTER TABLE ... SHRINK SPACE,但它有硬性前提:表必须启用行迁移支持(即 ROW MOVEMENT)且段管理是自动(ASSM)。
- 先开行迁移:
ALTER TABLE scott.emp ENABLE ROW MOVEMENT; - 再收缩(含压缩和 HWM 下移):
ALTER TABLE scott.emp SHRINK SPACE COMPACT;(先整理不移动 HWM)→SHRINK SPACE;(真正下推 HWM) - 索引会失效或进入
UNUSABLE状态,得手动ALTER INDEX ... REBUILD; - 如果表有
LOB字段,得加CASCADE参数,否则只处理主表段
为什么 SHRINK 后 RESIZE 还是失败?
因为 SHRINK SPACE 只影响表段,而一个数据文件里可能混着多个段(表、索引、LOB、undo、甚至其他用户的对象)。只要任一对象的 HWM 超过你目标尺寸,RESIZE 就会卡住。
- 查全文件所有段的最高 HWM:
SELECT MAX(block_id + blocks) * (SELECT value FROM v$parameter WHERE name = 'db_block_size') FROM dba_extents WHERE file_id = X; - 别只盯着大表,容易忽略大索引或历史未清理的临时表
- 如果文件里有
UNDO表空间或TEMP文件,它们不支持SHRINK,只能换文件迁移 - 某些系统表(如
WRH$_基表)即使没数据,也可能因延迟清理机制残留 Extent,需结合DBMS_SPACE.CLEAN_UP或 AWR 清理策略
更稳妥的收缩路径:MOVE + RESIZE 组合拳
当 SHRINK 太不可控或跨多个对象时,CREATE TABLE AS SELECT(CTAS)或 ALTER TABLE ... MOVE 更可靠,尤其适合单一大表占主导的场景。它彻底重建段,HWM 归零,但代价是锁表时间长、索引全失效、需额外空间。
- 移动前确保表空间有足够空闲空间容纳新段:
SELECT tablespace_name, bytes/1024/1024 MB FROM dba_free_space WHERE tablespace_name = 'USERS'; - 执行:
ALTER TABLE scott.emp MOVE TABLESPACE users;(同一表空间内 MOVE 也有效) - 立刻重建索引:
ALTER INDEX scott.emp_idx REBUILD; - 此时再查 HWM,基本落到最低点,
RESIZE几乎必成功 - 注意:
MOVE不保留原先的分区结构、物化视图日志、约束依赖等,复杂表要提前验证
真正难的从来不是命令敲对,而是搞清 HWM 落在哪、谁在挡路、以及要不要为了一次收缩停业务。很多 DBA 卡在第二步就放弃,其实第三步的全文件扫描才是关键动作。










