在ORACLE中,索引访问/查找(Index Lookup)路径有五种方式,分别为INDEX UNIQUE SCAN、INDEX RANGE SCAN、INDEX FULL SCAN、INDEX 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> 

对于组合唯一索引,如果索引前导列不在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 ROWID”或“SELECT 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> 

通常,执行计划先通过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

 

 

 

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索引访问路径总结的更多相关文章

  1. Oracle - index (索引)

        索引: 一种独立于表的模式对象, 可以存储在与表不同的磁盘或表空间中 @ 索引被删除或损坏, 不会对表产生影响, 其影响的只是查询的速度 @ 索引一旦建立, Oracle 管理系统会对其进行自 ...

  2. Oracle中组合索引的使用详解(转)

    在Oracle中可以创建组合索引,即同时包含两个或两个以上列的索引.在组合索引的使用方面,Oracle有以下特点: 1. 当使用基于规则的优化器(RBO)时,只有当组合索引的前导列出现在SQL语句的w ...

  3. oracle学习----访问路径

    什么是访问路径?表扫描数据的时候使用了什么方式,这个方式就是访问路径 1.全表扫描TABLE ACCESS FULL 全表扫描,多块读,等待事件:db file scattered read 如果是并 ...

  4. Oracle性能诊断艺术-学习笔记(索引访问方式)

    环境准备: 1.0 测试表 CREATE TABLE t ( id NUMBER, d1 DATE, n1 NUMBER, n2 NUMBER, n3 NUMBER, n4 NUMBER, n5 NU ...

  5. Oracle Index 索引无效原因

    索引无效原因 最近遇到一个SQL语句的性能问题,修改功能之前的运行时间平均为0.3s,可是添加新功能后,时间达到了4~5s.虽然几张表的数据量都比较大(都在百万级以上),但是也都有正确创建索引,不知道 ...

  6. yii框架美化访问路径,去掉index.php/?r=部分

    一.找到配置文件(ps:advance高级模板) 在工程目录-> backend目录 或 frontend目录 -> config目录 -> main.php文件 -> 在 r ...

  7. Oracle之虚拟索引

    一.引言 DBA在日常维护管理数据库进行低性能SQL分析时,有时候需要通过创建索引对SQL进行优化,但有些时候我们创建的索引是否能用到?这个只能创建以后才能看出效果,但是在实际工作中,特别是对大表创建 ...

  8. oracle提高之索引学习

    一. 索引介绍 1.1  索引的创建 语法 : CREATE UNIUQE | BITMAP INDEX <schema>.<index_name> ON <schema ...

  9. oracle hint 强制索引(转)

    oracle 1.建议建立一个以paytime,id,cost的复合索引.光是在paytime上建立索引会产生很多随机读.2.就算建立了索引,如果你查询的数据量很大的话,也不一定会用索引,有时候全表扫 ...

随机推荐

  1. HDU 1006 Digital Roots

    Problem Description The digital root of a positive integer is found by summing the digits of the int ...

  2. Code Complete-13/7/23

    What is "construction"?    Hava u ever  used construction paper to make some things?What i ...

  3. MongoDB高可用集群+MMS集群监控搭建

    备注: mongodb学习资料 http://www.runoob.com/mongodb/mongodb-tutorial.html 一. 集群的三个组件: mongos(query routers ...

  4. Docker 简述

    转自:https://cloud.tencent.com/developer/article/1354393 虚拟机和 docker 的区别,如下图: Image (镜像) 镜像不包含任何动态数据,其 ...

  5. Hadoop YARN架构设计要点

    YARN是开源项目Hadoop的一个资源管理系统,最初设计是为了解决Hadoop中MapReduce计算框架中的资源管理问题,但是现在它已经是一个更加通用的资源管理系统,可以把MapReduce计算框 ...

  6. k8s之external-etcd集群管理

    一.概述 kubernetes使用etcd作为数据中心,使用kubeadm部署kubernetes的时候默认会自己部署一个etcd,当然也可以将kubeadm部署的单点的etcd做成集群,但是比较麻烦 ...

  7. zookeeper服务发现实战及原理--spring-cloud-zookeeper源码分析

    1.为什么要服务发现? 服务实例的网络位置都是动态分配的.由于扩展.失败和升级,服务实例会经常动态改变,因此,客户端代码需要使用更加复杂的服务发现机制. 2.常见的服务发现开源组件 etcd—用于共享 ...

  8. Perl一行式:处理行号和单词数

    perl一行式程序系列文章:Perl一行式 所有行的行号 $ perl -pe '$_ = "$. $_"' file.log $ perl -ne 'print "$. ...

  9. MySQL系列详解一:MySQL&&多实例安装-技术流ken

    简介 MySQL是一个真正的多用户.多线程SQL数据库服务器.SQL(结构化查询语言)是世界上最流行的和标准化的数据库语言,它使得存储.更新和存取信息更加容易.MySQL是一个客户机/服务器结构的实现 ...

  10. [转]web串口调试助手,浏览器控制串口设备

    本文转自:https://blog.csdn.net/ldevs/article/details/39664697 打开串口时查找可用串口供选择 通过javascript调用activex控制串口收发 ...