索引碎片化会显著增加物理I/O次数,因逻辑连续的键值被分散在非相邻块中,导致范围扫描需访问更多离散块;虽不改变树高度,但大幅增加leaf_blocks数量,使优化器基于过时统计信息误判成本,引发实际I/O激增。
索引页碎片化直接增加物理I/O次数
oracle读取索引时,是以块(block)为单位从磁盘加载到buffer cache的。当索引段出现严重碎片化,原本逻辑上连续的索引键值被分散在多个非相邻的块中,导致一次范围扫描(如 where order_date between ...)需要访问更多离散块——哪怕只查100行数据,也可能触发数百次单块读(db file sequential read),而非预期的几十次。
关键点在于:碎片不改变索引树高度,但显著拉高叶节点块(leaf_blocks)的实际物理数量。查询优化器基于过时的统计信息估算成本,仍按“紧凑布局”规划执行路径,结果就是计划看起来合理,实际I/O爆炸。
碎片导致索引分裂加剧与缓存效率下降
高碎片索引在插入/更新时更容易触发叶节点分裂(尤其是90-10分裂),新分裂出的块往往无法复用原有空闲空间,进一步加剧空间浪费。更隐蔽的影响是:这些分散的小块难以被LRU算法有效保留在buffer cache中,频繁换入换出,使physical reads和buffer gets比值异常升高。
- 检查信号:
SELECT leaf_blocks, clustering_factor FROM dba_indexes WHERE index_name = 'IDX_ORD_DATE';,若leaf_blocks远大于理论值(如行数 ÷ 每块平均键数),且clustering_factor接近表行数,基本可判定碎片+数据分布差双重问题 - 对比验证:对同一查询运行两次
ALTER INDEX ... COALESCE;前后,观察v$sesstat中session logical reads和physical reads变化
重建索引时容易忽略的三个硬性条件
不是所有碎片都值得重建,也不是重建了就一定见效。以下三点没满足,ALTER INDEX ... REBUILD可能白忙:
- 统计信息未同步:重建后必须立刻执行
DBMS_STATS.GATHER_INDEX_STATS,否则优化器仍用旧的leaf_blocks和blevel估算成本 - 局部索引(LOCAL)需逐分区重建:对分区表执行
REBUILD默认只处理当前分区段,遗漏的分区仍保持碎片状态;应使用ALTER INDEX ... REBUILD PARTITION ...或指定UPDATE GLOBAL INDEXES - 高并发写场景下锁等待:
REBUILD需获取SS(sub-share)锁,若业务SQL正大量修改该索引列,会卡住重建进程,此时COALESCE(仅整理现有块)反而更安全
碎片化与索引失效常被混淆,但根源不同
索引失效(status = UNUSABLE)是元数据层面的“开关断开”,而碎片化是物理存储层面的“道路坑洼”。前者执行任何DML都会报错ORA-01502,后者全程静默——查询照跑、计划照出、只是越来越慢。最容易被忽略的是:当dba_ind_partitions.status显示USABLE,但dba_indexes.leaf_blocks持续膨胀,这大概率就是碎片在悄悄拖垮性能。











