oracle通过dba_extents计算高水位(hwm):hwm=max(block_id+blocks)×8192,高水位以上空闲空间=文件总大小−hwm字节数;dba_free_space不包含该空间因其未格式化;收缩前须确保目标大小≥hwm对应字节数。
查数据文件高水位以上空闲空间用 dba_extents 和 dba_data_files
oracle 不直接暴露“高水位以上空闲空间”这个字段,但可以通过 dba_extents 中每个文件的最高 block_id 推算出高水位(hwm),再和文件总大小对比得出。关键逻辑是:hwm 位置 = max(block_id) + blocks_in_that_extent,而高水位以上空间 = 文件总大小 − hwm 占用字节数。
常用查询结构如下(需有 SELECT ANY DICTIONARY 权限):
SELECT
a.file_id,
a.file_name,
ROUND(a.bytes / 1024 / 1024) AS total_mb,
ROUND(c.hwmsize / 1024) AS hwm_mb,
ROUND((a.bytes - c.hwmsize) / 1024 / 1024) AS free_above_hwm_mb
FROM dba_data_files a,
(SELECT file_id, MAX(block_id + blocks) * 8192 AS hwmsize
FROM dba_extents
GROUP BY file_id) c
WHERE a.file_id = c.file_id
AND a.tablespace_name = 'YOUR_TBS_NAME';
注意:MAX(block_id + blocks) 比单纯 MAX(block_id) 更准确,因为一个 extent 可能跨多个连续块;乘以 8192 是把块数转为字节(假设标准 8K 块大小)。
dba_free_space 查不到高水位以上空间的原因
dba_free_space 只返回已分配给段、但当前未使用的空闲块 —— 这些块全在高水位线以下。高水位线以上的区域从未被分配过,Oracle 认为它“未格式化”,不计入任何 free space 视图。
- 执行
SELECT COUNT(*) FROM dba_free_space WHERE file_id = X返回 0,并不意味没空闲空间,只说明没已分配的空闲块 - 如果某数据文件
dba_extents里查不到任何记录(即无 segment 分配),那它的整个文件都属于“高水位以上”,但此时MAX(block_id)会报 NULL,需加NVL处理 - 19c 中
autoextensible = 'YES'的文件,高水位以上空间可能被后续扩展自动占用,不能直接理解为“可安全收缩”
收缩前必须确认高水位是否真可降
即使计算出 free_above_hwm_mb > 0,也不能直接 RESIZE。Oracle 要求目标大小 ≥ 当前 HWM 对应的字节数。否则报错:ORA-03297: file contains used data beyond requested RESIZE value。
验证能否收缩的最小安全值:
SELECT file_id,
file_name,
ROUND(MAX(block_id + blocks) * 8192 / 1024 / 1024) + 1 AS min_resize_mb
FROM dba_extents
GROUP BY file_id, file_name;
- 结果中的
min_resize_mb是该文件能RESIZE到的最小整数 MB(向上取整) - 如果表空间启用了
SEGMENT SPACE MANAGEMENT AUTO,且存在大对象(LOB)段,dba_extents可能漏掉部分 extent,建议配合DBMS_SPACE.SPACE_USAGE校验 - 对临时表空间或 undo 表空间,此方法不适用 —— 它们不走普通段分配逻辑
用 DBMS_SPACE.SPACE_USAGE 查单个表段的 HWM 内部分布
当你要定位具体哪张表拖高了整个文件的 HWM,而不是粗略看文件级,就得下钻到段级别。这时 DBMS_SPACE.SPACE_USAGE 比 dba_extents 更可靠,它反映实际块使用状态(含 FS1–FS4 空闲状态、full、unformatted)。
示例(查用户表 EMP):
DECLARE
l_fs1_blocks NUMBER; l_fs2_blocks NUMBER; l_fs3_blocks NUMBER;
l_fs4_blocks NUMBER; l_full_blocks NUMBER; l_unformatted_blocks NUMBER;
BEGIN
SYS.dbms_space.space_usage(
segment_owner => 'SCOTT',
segment_name => 'EMP',
segment_type => 'TABLE',
fs1_blocks => l_fs1_blocks,
fs2_blocks => l_fs2_blocks,
fs3_blocks => l_fs3_blocks,
fs4_blocks => l_fs4_blocks,
full_blocks => l_full_blocks,
unformatted_blocks => l_unformatted_blocks
);
DBMS_OUTPUT.PUT_LINE('Full: ' || l_full_blocks);
DBMS_OUTPUT.PUT_LINE('Unformatted (above HWM): ' || l_unformatted_blocks);
END;
l_unformatted_blocks 就是这张表段内、真正位于高水位线之上的未格式化块数 —— 它和文件级的“高水位以上空间”不是一回事,但能帮你判断:是不是某张大表长期 delete+append 导致段内碎片堆积,进而把文件 HWM 顶得很高。
真正容易被忽略的是:高水位线不是静态值。哪怕你刚 shrink 了一张表,只要之后有 insert append 或 direct-path load,HWM 就可能立刻跳升。所以查完空间,得同步检查应用是否有这类写入模式,否则收缩只是暂时的。











