深入理解 Oracle 索引全扫描


什么是 Index Full Scan?

想象一本按姓氏排序的电话簿。

  • 你要看所有号码,且希望结果仍按姓氏排序。
  • 你当然可以直接从第一页翻到最后一页,从头到尾扫一遍
  • 电话簿本身就是有序的,因此你无需再排序

Oracle 的 索引全扫描(Index Full Scan) 就是干这件事:

  • 从索引根块 → 沿最左子树 → 依次读取所有叶块。
  • 使用 单块 I/Odb file sequential read),顺序由索引保证。
  • 结果天然有序,避免 Memory/Temp 排序

关键字:单块读、天然有序、无额外 SORT ORDER BY。

优化器为什么敢用 Index Full Scan?

官方文档给出了 3 条硬性条件,可简化为一句口诀:

“要么谓词用到索引列,要么索引包打天下且列非空,要么 ORDER BY 列非空。”

场景是否满足说明
WHERE department_id = 10谓词引用索引列(不必是前导列)。
SELECT department_id, department_name FROM departmentsdepartments 表的两列全在索引 DEPT_ID_PK(主键索引包含 ROWID),且 department_id NOT NULL。
ORDER BY department_idORDER BY 列非空。

只要命中其一,优化器就可能选择 INDEX FULL SCAN 而非 TABLE FULL SCAN + SORT。

执行计划长什么样?

SELECT department_id, department_name
FROM   departments
ORDER  BY department_id;
IdOperationNameCost
0SELECT STATEMENT2
1TABLE ACCESS BY INDEX ROWIDDEPARTMENTS2
2INDEX FULL SCANDEPT_ID_PK1
  • INDEX FULL SCAN 先有序地吐出 ROWID。
  • TABLE ACCESS BY INDEX ROWID 根据 ROWID 回表拿 department_name
  • 注意:没有 SORT ORDER BY 步骤,因为索引已保证顺序。

内部怎么扫?

  1. 从根块开始,定位到最左叶块(降序扫描则从右边开始)。
  2. 利用叶块的双向链表 向右水平遍历
  3. 每拿到一个索引条目,回表一次(单块 I/O)。
  4. 直到最后一个叶块,扫描结束。

什么时候 选 Index Full Scan?

  • 索引列允许 NULL,且查询条件为 IS NULL(无法定位)。
  • 查询列 不全是 索引列,需要回表次数太多(成本高于全表扫描)。
  • 统计信息过期,CBO 误判成本。
  • 需要并行扫描(Index Fast Full Scan 更适合多块 I/O)。
  1. 验证列非空SELECT nullable FROM user_tab_cols WHERE table_name='DEPARTMENTS';
  2. 查看索引列SELECT column_name FROM user_ind_columns WHERE index_name='DEPT_ID_PK';
  3. Hint 强制测试SELECT /*+ INDEX(departments dept_id_pk) */ … 观察执行计划。
  4. 监控排序事件SELECT name, value FROM v$mystat WHERE name='sorts (rows)'; 若 INDEX FULL SCAN 成功,该值不再增长。

当查询需要“全量 + 有序”的结果时,让索引自己走完全程,既是 I/O 最省,也是 CPU 最优的解法。

下次看到执行计划里出现 INDEX FULL SCAN,别再惊讶——那是 Oracle 在悄悄告诉你:“我已经替你省了一次排序。”

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部