高水位线(HWM)以下大量空间未被真实数据占用是表碎片最直接体现;需先更新统计信息,再通过blocks与num_rows×avg_row_len/db_block_size偏差、usage_rate低于0.3、DBMS_SPACE.SPACE_USAGE返回的fs1/fs2块远高于full_blocks等指标综合判断。

查表高水位线与实际使用率偏差
碎片最直接的体现是“高水位线(HWM)以下大量空间未被真实数据占用”。只要 blocks 远大于 num_rows * avg_row_len / db_block_size,就说明有明显浪费。
- 先确保统计信息最新:
EXEC DBMS_STATS.GATHER_TABLE_STATS('SCHEMA_NAME', 'TABLE_NAME'); - 执行判断查询(以 8KB 块大小为例):
SELECT table_name, blocks * 8 / 1024 AS hwm_mb, (num_rows * avg_row_len) / 1024 / 1024 AS used_mb, ROUND((num_rows * avg_row_len) / (blocks * 8192), 3) AS usage_rate FROM user_tables WHERE table_name = 'YOUR_TABLE'; - 若
usage_rate
看dba_free_space中碎片块数量
这不是查单个表,而是查整个表空间是否因长期分配/回收不均导致空闲空间离散。这对全表扫描性能影响隐蔽但严重。
- 仅对字典管理表空间有意义(
extent_management = 'DICTIONARY'):SELECT a.tablespace_name, COUNT(*) fragments FROM dba_free_space a JOIN dba_tablespaces b ON a.tablespace_name = b.tablespace_name WHERE b.extent_management = 'DICTIONARY' GROUP BY a.tablespace_name HAVING COUNT(*) > 20;
- 结果中
fragments超过 20,说明该表空间已出现较严重碎片;超过 500 就必须处理 - 自动段管理(ASSM)表空间不用看这个——它用位图管理,
dba_free_space记录的是逻辑空闲段,不是物理碎片指标
用DBMS_SPACE.SPACE_USAGE确认真实块级分布
上面两个方法都是估算或间接指标。DBMS_SPACE.SPACE_USAGE 是 Oracle 官方提供的精确段空间分析接口,能告诉你具体多少块是“全满”、“75%-100%满”、“50%-75%满”等。
- 只适用于 ASSM 表空间,且表不能含 BASICFILE LOB
- 典型调用:
DECLARE p_fs1_blocks NUMBER; -- 0-25% used p_fs2_blocks NUMBER; -- 25-50% used p_fs3_blocks NUMBER; -- 50-75% used p_fs4_blocks NUMBER; -- 75-100% used p_full_blocks NUMBER; -- 100% used BEGIN DBMS_SPACE.SPACE_USAGE( segment_owner => 'SCHEMA_NAME', segment_name => 'TABLE_NAME', segment_type => 'TABLE', unformatted_blocks => NULL, unformatted_bytes => NULL, fs1_blocks => p_fs1_blocks, fs1_bytes => NULL, fs2_blocks => p_fs2_blocks, fs2_bytes => NULL, fs3_blocks => p_fs3_blocks, fs3_bytes => NULL, fs4_blocks => p_fs4_blocks, fs4_bytes => NULL, full_blocks => p_full_blocks, full_bytes => NULL ); DBMS_OUTPUT.PUT_LINE('Full: ' || p_full_blocks); DBMS_OUTPUT.PUT_LINE('FS4 (75-100%): ' || p_fs4_blocks); DBMS_OUTPUT.PUT_LINE('FS3 (50-75%): ' || p_fs3_blocks); END; - 重点关注
p_fs1_blocks和p_fs2_blocks是否远高于p_full_blocks—— 这些就是“半空不空”的低效块,是碎片主力
注意统计信息过期会误导判断
所有基于 num_rows、avg_row_len、blocks 的计算,都依赖准确的统计信息。生产环境里,表刚删掉几百万行,但统计信息没更新,查询结果就会显示“使用率 95%”,完全掩盖碎片。
- 检查最后收集时间:
SELECT last_analyzed FROM dba_tables WHERE owner='SCHEMA_NAME' AND table_name='TABLE_NAME'; - 如果
last_analyzed是几个月前,或者刚执行过大批量DELETE/TRUNCATE,必须立刻收集:EXEC DBMS_STATS.GATHER_TABLE_STATS('SCHEMA_NAME', 'TABLE_NAME', CASCADE=>TRUE); -
CASCADE=>TRUE很关键——不加的话索引统计不更新,后续SHRINK SPACE CASCADE效果可能打折
真正容易被忽略的点在于:碎片不是“有没有”,而是“在哪一层”。表级碎片(SHRINK 可治)、索引级碎片(需 ALTER INDEX ... REBUILD ONLINE)、表空间级碎片(ALTER TABLESPACE ... COALESCE 或重建)、甚至 LOB 段碎片(DBMS_SPACE.SPACE_USAGE 第二种重载才适用)——每层都要用对工具,混用只会白忙。


















