ORACLE Index Lookup索引访问路径总结

 

ORACLE中,索引访问/查找(Index Lookup)路径有五种方式,分别为INDEX UNIQUE SCANINDEX RANGE SCANINDEX FULL SCANINDEX FAST FULL SCAN INDEX SKIP SCAN。下面通过一些案例介绍、总结一下这五种索引访问路径。本文是总结这方面的知识点,所以文中一些地方参考、引用了参考资料中的部分内容。详细、具体资料可以参考官方资料Index Scans

 

 

 

 

索引唯一扫描(INDEX UNIQUE SCAN

 

 

索引唯一扫描只发生在唯一性索引(UNIQUE INDEX)上,它仅仅适用于WHERE 条件中是等值查询的SQL,因为对于唯一索引,等值查询至多只会返回一条记录。对于组合唯一索引来说,WHERE条件需要包含所有的索引列才能使用索引唯一扫描(INDEX UNIQUE SCAN)。

 

 

SQL> SET AUTOTRACE TRACEONLY;
SQL> SELECT * FROM SCOTT.EMP
  2  WHERE EMPNO=7788;
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 2949544139
 
--------------------------------------------------------------------------------------
| Id  | Operation                   | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |        |     1 |    38 |     1   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| EMP    |     1 |    38 |     1   (0)| 00:00:01 |
|*  2 |   INDEX UNIQUE SCAN         | PK_EMP |     1 |       |     0   (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("EMPNO"=7788)
 
 
Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
          2  consistent gets
          0  physical reads
          0  redo size
        891  bytes sent via SQL*Net to client
        512  bytes received via SQL*Net from client
          1  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

 

 

 

索引唯一扫描(INDEX UNIQUE SCAN)仅仅适用于WHERE条件中是等值查询的SQL如果是查询条件是一个区间范围,则不会使用索引唯一扫描。如下所示,执行计划变成了索引范围扫描(INDEX RANGE SCAN)。

 

 

 

SQL> SET AUTOTRACE TRACEONLY;
SQL> SELECT * FROM SCOTT.EMP
  2  WHERE EMPNO>=788 AND EMPNO <=7788;
 
8 rows selected.
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 169057108
 
--------------------------------------------------------------------------------------
| Id  | Operation                   | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |        |    11 |   418 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| EMP    |    11 |   418 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | PK_EMP |    11 |       |     1   (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("EMPNO">=788 AND "EMPNO"<=7788)
 
 
Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
          4  consistent gets
          0  physical reads
          0  redo size
       1383  bytes sent via SQL*Net to client
        523  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          8  rows processed

 

 

 

 

注意:如下所示,如果查询条件为DEPTNO下的等值查询,由于字段DEPTNO上的索引为IX_DEPTNO(非唯一索引),所以不会出现索引唯一扫描。也就是说索引唯一扫描只发生在唯一索引上

 

 

SQL> SELECT * FROM SCOTT.EMP
  2  WHERE DEPTNO=10;
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 1198124189
 
-----------------------------------------------------------------------------------------
| Id  | Operation                   | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |           |     5 |   190 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| EMP       |     5 |   190 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IX_DEPTNO |     5 |       |     1   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("DEPTNO"=10)
 
 
Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
          4  consistent gets
          1  physical reads
          0  redo size
       1159  bytes sent via SQL*Net to client
        523  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          3  rows processed
 
SQL> 

 

 

对于组合唯一索引,WHERE条件需要包含所有的索引列才能使用索引唯一扫描。否则就会使用索引范围扫描(INDEX RANGE SCAN

 

 

 

SQL> CREATE TABLE SCOTT.EMP_TEST   
  2  AS
  3  SELECT * FROM SCOTT.EMP;
 
Table created.
 
SQL> CREATE UNIQUE INDEX SCOTT.IDX_EMP_TEST_U ON SCOTT.EMP_TEST(EMPNO, ENAME);
 
Index created.
 
SQL> SET AUTOTRACE ON EXPLAIN;
SQL> SELECT * FROM SCOTT.EMP_TEST
  2  WHERE EMPNO=7788;
 
     EMPNO ENAME      JOB              MGR HIREDATE         SAL       COMM     DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
      7788 SCOTT      ANALYST         7566 19-APR-87       3000                    20
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 206749441
 
----------------------------------------------------------------------------------------------
| Id  | Operation                   | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |                |     1 |    87 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| EMP_TEST       |     1 |    87 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IDX_EMP_TEST_U |     1 |       |     1   (0)| 00:00:01 |
----------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("EMPNO"=7788)
 
Note
-----
   - dynamic sampling used for this statement (level=2)
 
SQL> SELECT * FROM SCOTT.EMP_TEST
  2  WHERE EMPNO=7788 AND ENAME='SCOTT';
 
     EMPNO ENAME      JOB              MGR HIREDATE         SAL       COMM     DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
      7788 SCOTT      ANALYST         7566 19-APR-87       3000                    20
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 2899991080
 
----------------------------------------------------------------------------------------------
| Id  | Operation                   | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |                |     1 |    87 |     1   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| EMP_TEST       |     1 |    87 |     1   (0)| 00:00:01 |
|*  2 |   INDEX UNIQUE SCAN         | IDX_EMP_TEST_U |     1 |       |     0   (0)| 00:00:01 |
----------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("EMPNO"=7788 AND "ENAME"='SCOTT')
 
SQL> 

ORACLE Index Lookup索引访问路径总结_第1张图片

 

 

 

对于组合唯一索引,如果索引前导列不在WHERE条件中,那么执行计划路径就会走全表扫描而不会走索引扫描,如下所示:

 

 

 
SQL> SET AUTOTRACE ON EXPLAIN;
SQL> SELECT * FROM SCOTT.EMP_TEST
  2  WHERE ENAME='SCOTT';
 
     EMPNO ENAME      JOB              MGR HIREDATE         SAL       COMM     DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
      7788 SCOTT      ANALYST         7566 19-APR-87       3000                    20
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 3124080142
 
------------------------------------------------------------------------------
| Id  | Operation         | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |          |     1 |    38 |     3   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| EMP_TEST |     1 |    38 |     3   (0)| 00:00:01 |
------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("ENAME"='SCOTT')
 
SQL> 

 

 

 

另外,在执行计划中,索引唯一扫描的下一步,你会注意到有可能是TABLE ACCESS BY INDEX ROWIDSELECT STATEMENT部分。 TABLE ACCESS BY INDEX ROWID"这表示表数据没有通过FTS操作访问,而是通过rowid查找访问。 如果所有必需的数据都驻留在索引中,则表查找可能是不必要的,您将看到的只是索引访问(没有表访问)。 在以下示例中,所有列(只有EMPNO列)都在索引中。 所以,它不会进行表访问,而使用SELET * 就会产生表访问:

 

 

SQL> SET AUTOTRACE ON EXPLAIN;
SQL> SELECT EMPNO FROM SCOTT.EMP
  2  WHERE EMPNO=7788;
 
     EMPNO
----------
      7788
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 56244932
 
----------------------------------------------------------------------------
| Id  | Operation         | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |        |     1 |     4 |     0   (0)| 00:00:01 |
|*  1 |  INDEX UNIQUE SCAN| PK_EMP |     1 |     4 |     0   (0)| 00:00:01 |
----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("EMPNO"=7788)
 
SQL> SELECT * FROM SCOTT.EMP   
  2  WHERE EMPNO=7788;
 
     EMPNO ENAME      JOB              MGR HIREDATE         SAL       COMM     DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
      7788 SCOTT      ANALYST         7566 19-APR-87       3000                    20
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 2949544139
 
--------------------------------------------------------------------------------------
| Id  | Operation                   | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |        |     1 |    38 |     1   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| EMP    |     1 |    38 |     1   (0)| 00:00:01 |
|*  2 |   INDEX UNIQUE SCAN         | PK_EMP |     1 |       |     0   (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("EMPNO"=7788)
 
SQL> 

 

 

ORACLE Index Lookup索引访问路径总结_第2张图片

 

 

 

 

通常,执行计划先通过Index查找到数据对应的rowid值(对于非唯一索引可能返回多个rowid值),然后根据rowid直接从表中得到具体的数据,这种查询方式称为索引扫描或索引查找(index loopup)。一个rowid唯一表示一行数据,该行对应的数据块是通过一次I/O得到的,在此情况下该次I/O只会读取一个数据块。在索引中,除了存储每个索引的值外,索引还存储具有此值的行对应的ROWID的值

 

索引扫描可以由2步组成:

 

扫描索引得到对应的ROWID

通过找到的ROWID从表中读取具体的数据

 

每步都是单独的一次I/O,但是对于索引,由于经常使用,绝大多数都已经CACHE到内存中,所以第一步的I/O经常为逻辑I/O,即数据可以从内存中得到。但是对于第2步来说,如果表比较大,则其数据不可能全在内存中,所以其I/O很有可能是物理I/O,这是一个物理操作,相对逻辑I/O来说,是极其费时间的。所以如果对大表进行索引扫描,取出的数据如果大于总量的5%-10%,使用索引扫描会效率下降很多。但是如果查询的数据能全在索引中找到,就可以避免进行第2步操作,避免了不必要的I/O,此时即使通过索引扫描取出的数据比较多,效率还是很高的。进一步讲,如果SQL语句中对索引列进行排序,因为索引已经预先排序好了,所以在执行计划中不需要再次对索引列进行排序。

 

 

 

 

索引范围扫描(INDEX RANGE SCAN

 

 

 

索引范围扫描是一种很常见的表访问方式,引范围扫描的典型情况下是在谓词(WHERE限制条件)中使用范围操作符(<,>,<>,>=,<=,BEWTEEN)。下面是发生索引范围扫描的一些场景

 

 

1: 在唯一索引上使用范围查找操作符(>, <, <>, >=, <=, BEWTEEN)等。

 

2: 在组合唯一索引上,只使用部分列进行查询(一定包含前导列leading column),导致查询出多条记录(也有可能是一条记录)。

 

3: 在非唯一索引列上进行任何查询。

 

 

 

1: 在唯一索引上使用范围查找操作符(>, <, <>, >=, <=, BEWTEEN)等。

 

 

 

SQL> SET AUTOTRACE ON EXPLAIN;
SQL> SELECT * FROM SCOTT.EMP
  2  WHERE EMPNO >=7788;
 
     EMPNO ENAME      JOB              MGR HIREDATE         SAL       COMM     DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
      7788 SCOTT      ANALYST         7566 19-APR-87       3000                    20
      7839 KING       PRESIDENT            17-NOV-81       5000                    10
      7844 TURNER     SALESMAN        7698 08-SEP-81       1500          0         30
      7876 ADAMS      CLERK           7788 23-MAY-87       1100                    20
      7900 JAMES      CLERK           7698 03-DEC-81        950                    30
      7902 FORD       ANALYST         7566 03-DEC-81       3000                    20
      7934 MILLER     CLERK           7782 23-JAN-82       1300                    10
 
7 rows selected.
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 169057108
 
--------------------------------------------------------------------------------------
| Id  | Operation                   | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |        |     5 |   190 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| EMP    |     5 |   190 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | PK_EMP |     5 |       |     1   (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("EMPNO">=7788)
 
SQL> 

 

 

2: 在组合唯一索引上,只使用部分列进行查询(一定包含前导列leading column ),导致查询出一条或多条记录

 

 

 

SQL> SET AUTOTRACE OFF;
SQL> SET AUTOTRACE ON EXPLAIN;
SQL> SELECT * FROM SCOTT.EMP_TEST
  2  WHERE EMPNO=7788;
 
     EMPNO ENAME      JOB              MGR HIREDATE         SAL       COMM     DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
      7788 SCOTT      ANALYST         7566 19-APR-87       3000                    20
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 206749441
 
----------------------------------------------------------------------------------------------
| Id  | Operation                   | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |                |     1 |    38 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| EMP_TEST       |     1 |    38 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IDX_EMP_TEST_U |     1 |       |     1   (0)| 00:00:01 |
----------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("EMPNO"=7788)
 
SQL> 

 

 

 

3: 在非唯一索引列上进行任何查询。

 

 

 

SQL> SET AUTOTRACE TRACEONLY;
SQL> SELECT * FROM SCOTT.EMP
  2  WHERE DEPTNO=20;
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 1198124189
 
-----------------------------------------------------------------------------------------
| Id  | Operation                   | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |           |     5 |   190 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| EMP       |     5 |   190 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IX_DEPTNO |     5 |       |     1   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("DEPTNO"=20)
 
 
Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
          4  consistent gets
          0  physical reads
          0  redo size
       1241  bytes sent via SQL*Net to client
        523  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          5  rows processed
 
SQL> SELECT * FROM SCOTT.EMP
  2  WHERE DEPTNO >=10;
 
14 rows selected.
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 1198124189
 
-----------------------------------------------------------------------------------------
| Id  | Operation                   | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |           |    14 |   532 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| EMP       |    14 |   532 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IX_DEPTNO |    14 |       |     1   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("DEPTNO">=10)
 
 
Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
          4  consistent gets
          0  physical reads
          0  redo size
       1630  bytes sent via SQL*Net to client
        523  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
         14  rows processed
 
SQL> 

 

 

索引降序范围扫描(INDEX RANGE SCAN DESCENDING

 

 

在默认情况下,索引是按照索引关键字升序来存放数据的。当SQL语句使用ORDER BY COLUMN_NAME DESC排序时,就会出现INDEX RANGE SCAN DESCENDING的访问路径

 

按照常理来讲,如果有ORDER BY COLUMN_NAME DESC的条件,那么

 

① 需要先从索引中读取数据;

 

② 再按照条件中COLUMN_NAME字段做降序排序。

 

而选择INDEX RANGE SCAN DESCENDING,则是直接在索引上按照索引关键字降序查找数据(其实是找到对应索引最右边的叶子块的第一行索引行,然后通过叶子块之间的双向链表访问数据),这样正是为了避免先按照索引来查找数据,然后再做一次降序排序的操作。如下测试所示:

 

 

SQL> SET LINESIZE 1200;
SQL> SET AUTOTRACE TRACEONLY;
SQL> SELECT * FROM SCOTT.EMP_TEST
  2  WHERE EMPNO >= 7788 
  3  ORDER BY EMPNO DESC;
 
7 rows selected.
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 4203867900
 
-----------------------------------------------------------------------------------------------
| Id  | Operation                    | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |                |     5 |   190 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID | EMP_TEST       |     5 |   190 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN DESCENDING| IDX_EMP_TEST_U |     5 |       |     1   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("EMPNO">=7788 AND "EMPNO" IS NOT NULL)
 
 
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          4  consistent gets
          0  physical reads
          0  redo size
       1324  bytes sent via SQL*Net to client
        523  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          7  rows processed
 
SQL> 

 

 

 

索引全扫描(INDEX FULL SCAN

 

 

索引全扫描需要扫描目标索引所有叶子块的所有索引行。但是这不意味着索引全扫描需要扫描该索引的所有分支块。在默认情况下,索引全局扫描只需要通过访问必要的分支块定位到该索引最左边的叶子块的第一行索引行。然后就可以利用该索引叶子块之间的双向指针链表,从左至右依次顺序扫描该索引所有叶子块的所有索引行了。索引全扫描的结果是有序的。它是根据索引的键值列来排序的。也就是说如果走索引全局扫描能达到排序的效果。这样就可以避免对该索引的索引键值再做排序操作。但是,也正是索引全扫描的有序性决定了索引全扫描不能够并行执行。如果查询列全部是目标索引的索引列,那么索引全扫描是不需要回表的。

 

 

SQL> CREATE INDEX SCOTT.IX_EMP_N1 ON SCOTT.EMP(EMPNO, ENAME);
 
Index created.
 
SQL> SET AUTOTRACE TRACEONLY;
 
SQL> select empno, ename from scott.emp order by empno,ename;
 
14 rows selected.
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 162731191
 
------------------------------------------------------------------------------
| Id  | Operation        | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------
|   0 | SELECT STATEMENT |           |    14 |   140 |     1   (0)| 00:00:01 |
|   1 |  INDEX FULL SCAN | IX_EMP_N1 |    14 |   140 |     1   (0)| 00:00:01 |
------------------------------------------------------------------------------
 
Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
          2  consistent gets
          0  physical reads
          0  redo size
        837  bytes sent via SQL*Net to client
        523  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
         14  rows processed
 
SQL> 

 

 

 

 

索引快速扫描(INDEX FAST FULL SCAN

 

 

索引快速扫描和索引全扫描一样,也需要扫描目标索引所有的叶子块的所有索引行。它也适用于所有类型的B树索引(包括唯一性索引和非唯一性索引)。 索引快速全扫描与索引全扫描相比有以下三点区别:

 

    1: 索引快速全扫描只适用于CBO模式,而索引全扫描可以用于CBO也可以用于RBO

    2: 索引快速全扫描可以使用多块读,也可以并行执行。这种存取方法中,可以使用多块读功能,也可以使用并行读入,以便获得最大吞吐量与缩短执行时间

    3: 索引快速全扫描的执行结果不一定是有序的,因为索引快速全扫描是根据索引在磁盘上的物理存储顺序来扫描的,而不是根据索引行的逻辑顺序来扫描的。所以扫描结果不一定有序(对于单个索引叶子块中的索引行而言,其物理存储顺序和逻辑存储顺序是一致的;但对于物理存储位置相邻的索引叶子块而言,块与块之间索引行的物理存储顺序则不一定在逻辑上有序)

 

 

SQL> BEGIN
  2     FOR IndexLoop IN 0..1000 LOOP
  3             INSERT INTO SCOTT.EMP(EMPNO,ENAME) 
  4             VALUES(IndexLoop,CONCAT('TEST',IndexLoop));
  5     END LOOP;
  6  END;
  7  / 
PL/SQL procedure successfully completed.
 
SQL> execute dbms_stats.gather_table_stats(ownname => 'SCOTT', tabname =>'EMP',
     estimate_percent =>DBMS_STATS.AUTO_SAMPLE_SIZE, method_opt => 'FOR ALL COLUMNS SIZE AUTO');
 
PL/SQL procedure successfully completed.
SQL> SELECT EMPNO FROM SCOTT.EMP;
 
1015 rows selected.
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 366039554
 
-------------------------------------------------------------------------------
| Id  | Operation            | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |        |  1015 |  4060 |     3   (0)| 00:00:01 |
|   1 |  INDEX FAST FULL SCAN| PK_EMP |  1015 |  4060 |     3   (0)| 00:00:01 |
-------------------------------------------------------------------------------
 
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
         72  consistent gets
          0  physical reads
          0  redo size
      18050  bytes sent via SQL*Net to client
       1260  bytes received via SQL*Net from client
         69  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
       1015  rows processed

 

 

 

索引跳跃扫描(INDEX SKIP SCAN

 

 

 

当表有一个复合索引,而在查询中除了索引中第一列外的其他列作为条件,并且优化器模式为CBO,这时候查询计划就有可能使用到索引跳跃扫描Skip scan会探测出索引前导列的唯一值个数,每个唯一值都会作为常规扫描的入口,在此基础上做一次查找,最后合并这些查询

 

 

INDEX SKIP SCAN,发生在多个列建立的复合索引上,如果SQL中谓词条件只包含索引中的部分列,并且这些列不包含建立索引时的第一列(前导列)时,就可能发生INDEX SKIP SCAN。索引跳跃扫描仅仅适用于那些目标索引前导列的DISTINCT值数量较少、后续非前导列的可选择性有非常好的情况下。

 

   

Oracle 10g的文档如下:

 

 

Index Skip Scans

 

Index skip scans improve index scans by nonprefix columns. Often, scanning index

blocks is faster than scanning table data blocks.

Skip scanning lets a composite index be split logically into smaller subindexes. In skip

scanning, the initial column of the composite index is not specified in the query. In

other words, it is skipped.

The number of logical subindexes is determined by the number of distinct values in

the initial column. Skip scanning is advantageous if there are few distinct values in the

leading column of the composite index and many distinct values in the nonleading

key of the index.

 

 

Example 13–5 Index Skip Scan

 

Consider, for example, a table employees (sex, employee_id, address) with a

composite index on (sex, employee_id). Splitting this composite index would result

in two logical subindexes, one for M and one for F.

For this example, suppose you have the following index data:

('F',98)

('F',100)

('F',102)

('F',104)

('M',101)

('M',103)

('M',105)

 

 

 

The index is split logically into the following two subindexes:

 

 

The first subindex has the keys with the value F.

The second subindex has the keys with the value M.

 

Figure 13–2 Index Skip Scan Illustration

 

ORACLE Index Lookup索引访问路径总结_第3张图片

 

 

 

The column sex is skipped in the following query:

SELECT *

FROM employees

WHERE employee_id = 101;

A complete scan of the index is not performed, but the subindex with the value F is

searched first, followed by a search of the subindex with the value M.

 

 

 

 

SQL> DROP TABLE SCOTT.OBJECT_TEST;
 
Table dropped.
 
SQL> CREATE TABLE SCOTT.OBJECT_TEST
  2  AS
  3     SELECT * FROM DBA_OBJECTS;
 
Table created.
 
SQL> CREATE INDEX SCOTT.IDX_OBJECT_TEST_N1 ON SCOTT.OBJECT_TEST(OWNER, OBJECT_ID, OBJECT_TYPE);
 
Index created.
 
SQL> execute dbms_stats.gather_table_stats(ownname => 'SCOTT', tabname =>'OBJECT_TEST');
 
PL/SQL procedure successfully completed.
 
SQL>
SQL> SET AUTOTRACE TRACEONLY;
SQL> SELECT * FROM SCOTT.OBJECT_TEST
  2  WHERE OBJECT_ID=13;
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 1063825859
 
--------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name               | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |                    |     1 |    97 |    28   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| OBJECT_TEST        |     1 |    97 |    28   (0)| 00:00:01 |
|*  2 |   INDEX SKIP SCAN           | IDX_OBJECT_TEST_N1 |     1 |       |    27   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("OBJECT_ID"=13)
       filter("OBJECT_ID"=13)
 
 
Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
         18  consistent gets
          0  physical reads
          0  redo size
       1607  bytes sent via SQL*Net to client
        523  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed
 
SQL

 

 

 

参考资料:

 

http://blog.itpub.net/26736162/viewspace-2139246/

 

http://docs.oracle.com/cd/B19306_01/server.102/b14211/optimops.htm#i51571 

 

https://databaseinternalmechanism.com/oracle-database-internals/index-lookup-unique-scanrange-scan-full-scan-fast-full-scan-skip-scan/

 

 

你可能感兴趣的:(ORACLE Index Lookup索引访问路径总结)