在 Oracle 中,除了常见的「索引范围扫描」「全表扫描」之外,还有一类常被忽视却极具性价比的访问路径——Index Fast Full Scan(IFFS)。它把索引本身当成一张「瘦表」来读,不经过表,多块 I/O 直扫所有叶块,常能在「只查索引列」的场景下带来意想不到的 I/O 与性能收益。
IFFS 是什么?一句话记住它
把索引当成表,多块 I/O 全扫所有叶块;不走 ROWID 回表,不按顺序返回行。
关键词:瘦表、多块读、无序返回、不回表。
触发条件:优化器什么时候会选 IFFS?
必要条件 | 说明 |
---|---|
查询列全部在索引里 | 任何需要的列都包含在索引键或包含列中,无需回表。 |
统计信息可信 | 优化器认为扫描索引的代价比全表扫描低。 |
无排序需求 | IFFS 无法按索引顺序返回数据,若 SQL 有 ORDER BY 与索引顺序一致,则优化器倾向选择 INDEX FULL SCAN(有序)。 |
工作原理拆解
- 多块 I/O:一次 DB_FILE_MULTIBLOCK_READ_COUNT 个块,像全表扫描一样高效。
- 读取结构:根块、分支块、叶块全部读入缓存,但仅处理叶块中的索引条目。
- 并行友好:可以开启并行,典型 OLAP/报表场景利器。
- 无法避免排序:因为叶块可能按块顺序而非键顺序返回,需要额外 SORT 操作才能满足
ORDER BY
。
强制 IFFS:hint 语法
SELECT /*+ INDEX_FFS(<表别名> <索引名>) */ ...
示例:统计 departments 表总行数,索引已覆盖所需列。
SELECT /*+ INDEX_FFS(departments dept_id_pk) */ COUNT(*)
FROM departments;
执行计划:
-------------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 2 (100) | |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | INDEX FAST FULL SCAN| DEPT_ID_PK | 27 | 2 (0) | 00:00:01 |
Cost 只有 2,远低于全表扫描。
5. 典型应用场景
场景 | 示例 SQL | 收益 |
---|---|---|
聚合统计 | SELECT COUNT(*) FROM sales WHERE sale_date >= DATE '2025-01-01' | 索引已包含 sale_date,无需回表。 |
去重计数 | SELECT COUNT(DISTINCT customer_id) FROM orders | 覆盖索引避免表扫描。 |
报表列投影 | SELECT id, name FROM departments | 只有两列,且都在索引中。 |
6. 注意事项与踩坑
- 不回表 ≠ 一定更快:若索引远大于表(高碎片、低删除空间),全表扫描可能更优。
- 无法避免排序:需要排序时,额外 SORT 可能抵消 I/O 优势。
- 并行度:
/*+ INDEX_FFS PARALLEL(4) */
可提速,但会占用 CPU 与 I/O。 - 统计信息:缺失或过期时,优化器可能误判成本,导致走错计划。
维度 | INDEX FAST FULL SCAN |
---|---|
适用 | 查询列全部在索引内 |
优点 | 像扫表一样扫索引,I/O 少,可并行 |
缺点 | 无序返回,无法避免排序 |
关键词 | INDEX_FFS hint、多块读、不回表 “列全在索引,不回表;多块 I/O 跑得快,顺序乱要排序。” |