不可见索引简介

从Oracle 11g开始,可以创建不可见索引(invisible index)。默认情况下,优化器会忽略invisible index,不使用invisible index,即使添加了相关索引hint,也不会使用invisible index。


初始化参数optimizer_use_invisible_indexes决定优化器是否使用invisible index,其默认值为false,即默认不使用invisible index。但如果在session级别或者system级别上将optimizer_use_invisible_indexes初始化参数设置为true,那么就可以使用invisible index。

与不可用索引(unusable index)不同,invisible index在使用DML语句期间仍会得到维护


Oracle引入不可见索引是有用途的,使索引不可见是使索引不可用或者删除索引的一种替代办法。
在删除索引之前,将索引修改为不可见,观察是否会产生影响,以便判断索引是否可以删除。
当索引不可见时,优化器生成的执行计划不会使用该索引。删除索引时,可以先将索引修改为invisible,如果未发生性能下降问题,则可以删除该索引。在表上新建索引时,可以先创建一个最初不可见的索引,然后执行测试,看索引的效率怎么样,最后确定是否使该索引可见,是否使用该索引。


可以查看dba_indexes、all_indexes、user_indexes视图的visibility字段来确定该索引是可见索引还是不可见索引,visible表示可见,invisible表示不可见。

不可见索引测试

下面做一些简单的测试。

创建不可见索引

先创建tab表,然后在表上创建了一个invisible索引。

SQL> create table tab as select * from user_objects;
Table created.
SQL> create index tab_idx1 on tab(object_name) invisible; 
Index created.
SQL> col INDEX_NAME for a25
SQL> col TABLE_OWNER for a20
SQL> col TABLE_NAME for a20
SQL> col VISIBILITY for a25
SQL> select INDEX_NAME,TABLE_OWNER,TABLE_NAME,VISIBILITY,STATUS from user_indexes where TABLE_NAME='TAB';
INDEX_NAME TABLE_OWNER TABLE_NAME VISIBILITY  STATUS
----------                -----------        ----------  ----------- --------
TAB_IDX1                   SCOTT                 TAB   INVISIBLE

测试优化器是否会忽略不可见索引

SQL> set autotrace traceonly
SQL> select * from tab where object_name='EMP';Execution Plan
----------------------------------------------------------
Plan hash value: 1995730731--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |         |     1 |   190 |     3   (0)| 00:00:01    |
|*  1 |  TABLE ACCESS FULL| TAB  |     1 |   190 |     3   (0)| 00:00:01    |
--------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------   1 - filter("OBJECT_NAME"='EMP')
Note
-----
   - dynamic sampling used for this statement (level=2)Statistics
----------------------------------------------------------
         17  recursive calls
          0  db block gets
         33  consistent gets
          0  physical reads
          0  redo size
       1328  bytes sent via SQL*Net to client
        419  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed
SQL> select /*+index(tab tab_idx1)*/ * from tab where object_name='EMP';
Execution Plan
----------------------------------------------------------
Plan hash value: 1995730731--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |         |     1 |   190 |     3   (0)| 00:00:01    |
|*  1 |  TABLE ACCESS FULL| TAB  |     1 |   190 |     3   (0)| 00:00:01    |
--------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------   1 - filter("OBJECT_NAME"='EMP')
Note
-----
   - dynamic sampling used for this statement (level=2)Statistics
----------------------------------------------------------
          4  recursive calls
          0  db block gets
          9  consistent gets
          0  physical reads
          0  redo size
       1328  bytes sent via SQL*Net to client
        419  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

从上面的两个执行计划看出,都没有走invisible index(即使加了hint也被忽略了),均走全表扫描。说明默认情况下,优化器会忽略invisible index,不使用invisible index。

测试优化器是否会使用可见索引

将invisible index修改为visible index,观察优化器会不会使用索引TAB_IDX1。

SQL> alter index tab_idx1 visible; 
Index altered.
SQL> set autotrace traceonly
SQL> select * from tab where object_name='EMP';Execution Plan
----------------------------------------------------------
Plan hash value: 2166198891---------------------------------------------------------------------------------------------------
| Id  | Operation                                       | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                       |                 |     1    |   190  |     2   (0)|       00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID | TAB          |     1    |   190  |     2   (0)|       00:00:01 |
|*  2 |   INDEX RANGE SCAN                   | TAB_IDX1 |     1    |           |     1   (0)|       00:00:01 |
---------------------------------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------   2 - access("OBJECT_NAME"='EMP')
Note
-----
   - dynamic sampling used for this statement (level=2)Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          3  consistent gets
          0  physical reads
          0  redo size
       1331  bytes sent via SQL*Net to client
        419  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

TAB_IDX1索引由invisible修改为visible后,执行计划走了TAB_IDX1索引。

测试参数optimizer_use_invisible_indexes对不可见索引的影响

下面测试参数optimizer_use_invisible_indexes对不可见索引的影响。这里仅在session级做测试。

将TAB_IDX1索引由visible index修改为invisible index。

SQL> alter index tab_idx1 invisible;
Index altered.
查看optimizer_use_invisible_indexes参数的值,默认为false。
SQL> show parameter optimizer_use_invisible_indexesNAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
optimizer_use_invisible_indexes      boolean     FALSE

将参数值修改为true,优化器走了TAB_IDX1,使用了invisible index。

SQL> alter session set optimizer_use_invisible_indexes=true;
Session altered.
SQL>
SQL> set autotrace traceonly
SQL>
SQL> select * from tab where object_name='EMP';Execution Plan
----------------------------------------------------------
Plan hash value: 2166198891---------------------------------------------------------------------------------------------------
| Id  | Operation                                       | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                       |                 |     1    |   190  |     2   (0)|       00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID | TAB          |     1    |   190  |     2   (0)|       00:00:01 |
|*  2 |   INDEX RANGE SCAN                   | TAB_IDX1 |     1    |           |     1   (0)|       00:00:01 |
---------------------------------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------   2 - access("OBJECT_NAME"='EMP')
Note
-----
   - dynamic sampling used for this statement (level=2)Statistics
----------------------------------------------------------
          9  recursive calls
          2  db block gets
          9  consistent gets
          0  physical reads
          0  redo size
       1331  bytes sent via SQL*Net to client
        419  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed
SQL> set autotrace off

将参数值修改为false,优化器走了全表扫描,没有使用invisible index。

SQL> alter session set optimizer_use_invisible_indexes=false; 
Session altered.
SQL> set autotrace traceonly
SQL>
SQL> select * from tab where object_name='EMP';Execution Plan
----------------------------------------------------------
Plan hash value: 1995730731--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |         |     1 |   190 |     3   (0)| 00:00:01    |
|*  1 |  TABLE ACCESS FULL| TAB  |     1 |   190 |     3   (0)| 00:00:01    |
-------------------------------------------------------------------------- 
Predicate Information (identified by operation id):
---------------------------------------------------   1 - filter("OBJECT_NAME"='EMP')
Note
-----
   - dynamic sampling used for this statement (level=2)Statistics
----------------------------------------------------------
          5  recursive calls
          0  db block gets
          9  consistent gets
          0  physical reads
          0  redo size
       1328  bytes sent via SQL*Net to client
        419  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processedSQL> set autotrace off


经过测试,发现在会话级将optimizer_use_invisible_indexes参数设置为true,优化器会使用invisible索引。在会话级将optimizer_use_invisible_indexes参数设置为false,优化器不会使用invisible索引。

不可见索引测试总结

通过测试,可知优化器默认会忽略invisible index,不使用invisible index,要想使用invisible index,需要将optimizer_use_invisible_indexes参数修改为true,才能使用或者将invisible index修改visible index,这样也可以使用该索引。
随着invisible index的引入,给索引的维护管理工作带来了很多便利,所以日常运维时可以尝试使用invisible index,提高工作效率。