AWR报告中“Top Segments by Physical Reads”可暴露高碎片索引,需重点检查带INDEX后缀对象的PHYSICAL_READS是否异常偏高,并结合SQL执行计划及DBMS_SPACE.SPACE_USAGE验证空间利用率与B树健康度。

直接查 AWR 里 “Top Segments by Physical Reads”
高碎片索引不会在 AWR 报告里标出“碎片率 XX%”,但会通过物理读暴增暴露自己。打开 AWR 报告,翻到 Top Segments by Physical Reads 章节(通常在 Instance Activity Stats 后面),重点看 OBJECT_NAME 列中带 INDEX 后缀的行(比如 ORDERS_IDX_STATUS),再比对它的 PHYSICAL_READS 是否远高于同表其他索引、或明显高于历史均值。
如果这个索引对应的表,在 SQL ordered by Physical Reads 或 SQL ordered by Gets 里高频出现,且执行计划里全是 TABLE ACCESS FULL,基本可断定:索引结构失衡(B树深度过大、叶块空洞多)或统计信息失效,导致优化器弃用它。
注意避开两类干扰项:
-
LOBSEGMENT类型段——它们可能是函数索引或 LOB 索引,在DBA_SEGMENTS中类型不是INDEX,需额外确认 - 刚上线或低频查询的索引——物理读高可能只是因为首次加载,而非碎片问题
用 DBMS_SPACE.SPACE_USAGE 查真实空间利用率
DBMS_SPACE.SPACE_USAGE 是 Oracle 唯一能返回段内块级空间分布的内置过程,比基于 blocks/num_rows 的估算更准,尤其适用于 ASSM 表空间。
执行前确保:
- 目标段所在表空间是
AUTO段空间管理(SEGMENT SPACE MANAGEMENT AUTO) - 当前用户有
EXECUTE权限 onDBMS_SPACE - 不用于 SECUREFILE LOB(要用另一重载)
典型调用方式:
DECLARE
unf NUMBER; unfb NUMBER;
fs1 NUMBER; fs1b NUMBER;
fs2 NUMBER; fs2b NUMBER;
fs3 NUMBER; fs3b NUMBER;
fs4 NUMBER; fs4b NUMBER;
full NUMBER; fullb NUMBER;
BEGIN
DBMS_SPACE.SPACE_USAGE(
segment_owner => 'SCHEMA_NAME',
segment_name => 'YOUR_INDEX_NAME',
segment_type => 'INDEX',
unformatted_blocks => unf,
unformatted_bytes => unfb,
fs1_blocks => fs1,
fs1_bytes => fs1b,
fs2_blocks => fs2,
fs2_bytes => fs2b,
fs3_blocks => fs3,
fs3_bytes => fs3b,
fs4_blocks => fs4,
fs4_bytes => fs4b,
full_blocks => full,
full_bytes => fullb
);
DBMS_OUTPUT.PUT_LINE('Used %: ' || ROUND(fullb * 100 / NULLIF(fullb + fs1b + fs2b + fs3b + fs4b, 0), 2));
END;关键判断点:full_bytes 占总块字节数比例 fs1b + fs2b(空闲但可重用的块)占比显著上升,说明叶块大量删除后未合并,碎片已影响访问效率。
结合 INDEX_STATS 看索引结构健康度
ANALYZE INDEX ... VALIDATE STRUCTURE 虽是旧方法,但仍是诊断 B 树结构是否失衡的最直接手段——它会把索引结构快照写入 INDEX_STATS 视图,无需采样、不依赖统计信息。
操作步骤:
- 先执行
ANALYZE INDEX your_idx_name VALIDATE STRUCTURE ONLINE;(加ONLINE避免锁表) - 立刻查
SELECT name, height, del_lf_rows, lf_rows, ROUND((del_lf_rows / lf_rows) * 100, 2) AS "碎片率(%)" FROM index_stats;
重点关注三项:
-
height > 4:B树过深,每次索引查找要多层 IO -
del_lf_rows / lf_rows > 20%:叶块中已删除键值占比过高,扫描时跳过开销大 pct_used :叶块平均填充率偏低,说明插入无序或频繁 delete 后未重组
注意:VALIDATE STRUCTURE 不支持分区索引整体分析,必须指定单个分区名;也不支持位图索引。
为什么不能只看 dba_tables.blocks/num_rows 估算碎片?
这种算法(如 (blocks * 8192 - num_rows * avg_row_len) / (blocks * 8192))误差极大,原因很实在:
-
avg_row_len是统计信息里的平均值,实际行长度分布可能极偏斜(比如 varchar2 字段大量为空或超长) -
blocks包含高水位线以上未格式化的块,也包含 PCTFREE 预留空间,但这些块未必真“浪费” - 完全忽略 ITL、行迁移、链式行的影响——这三者会让单行跨多个块,大幅拉低有效空间利用率
所以它只能当粗筛工具(比如快速扫出 USED_RATE 的表),但一旦要定位“为什么索引失效”,就必须落到 <code>DBMS_SPACE 和 INDEX_STATS 这两级实测数据上。线上环境里,靠估算做重建决策,容易把好索引干掉,反而让 SQL 更慢。


















