今天刷题的时候看到一道有关 INVISIBLE index 的题目,没想到居然选错了。随即在网上找相关知识点牢记一下。

不可见索引概念

不可见索引(Invisible Index)是ORACLE 11g引入的新特性。不可见索引是会被优化器忽略的不可见索引,除非在会话或系统级别上将OPTIMIZER_USE_INVISIBLE_INDEXES初始化参数显式设置为TRUE。此参数的默认值是FALSE。

如果是虚拟索引是为了合理、科学新增索引而设计的,那么不可见索引就是为了合理、科学的删除索引而设计的。为什么这样说呢? 因为DBA在维护索引时,我们经常会找出无用或低效的索引,并删除这些索引,在生产环境下,删除索引还是有一定风险的,即使ORACLE提供了监控索引使用情况的技术。例如,某些索引可能只是在一些周期的作业中被使用到,而如果监控周期没有覆盖到这些作业的触发点,就会认为索引是无用的而被删除。当作业启动后,可能就会对系统性能造成冲击。这时,可能就会手忙脚乱的去找回索引定义语句、重建索引。

11G之前,我们可以先不删除索引,而将其修改为unusable。这样的话,索引的定义并未删除,只是索引不能再被使用也不会随着表数据的更新而更新。当需要重新使用该索引时,需要用rebuild语句重建、然后更新统计信息。对于一些大表来说,这个时间可能就非常长。在ORACLE 11g里提供了一个新的特性来降低直接删除索引或者禁用索引的风险,那就是索引不可见(Index Invisible)。我们可以将无用或低效的索引设置为不可见索引,当观察一段时间后,发现其对系统性能并无任何影响,那么就可以彻底删除索引了。

实验测试

创建测试表TEST,在表上新建不可见索引IDX_TEST_ID,不可见索引可以从字段VISIBILITY这个字段来区别,如下所示:

 SQL> create table test as select * from dba_objects;
 
 SQL> create index idx_test_id on test(object_id) invisible;

SQL>  select index_name, status,visibility from dba_indexes where index_name=upper('idx_test_id');

INDEX_NAME     STATUS   VISIBILITY
------------ -------- ----------
IDX_TEST_ID    VALID    INVISIBLE

#收集下统计信息
EXEC DBMS_STATS.GATHER_TABLE_STATS(OWNNAME =>'C##zhangyan',TABNAME=>'TEST',CASCADE => TRUE);

SQL> set autotrace traceonly;
SQL> select * from test where object_id=12;
Execution Plan
----------------------------------------------------------
Plan hash value: 1357081020

--------------------------------------------------------------------------
| Id  | Operation	  | Name | Rows  | Bytes | Cost (%CPU)| Time	 |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |	 |     1 |   132 |   398   (1)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| TEST |     1 |   132 |   398   (1)| 00:00:01 |
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("OBJECT_ID"=12)


Statistics
----------------------------------------------------------
	  2  recursive calls
	  6  db block gets
       1433  consistent gets
	  0  physical reads
       1372  redo size
       2686  bytes sent via SQL*Net to client
	399  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(text, idx_test_id) */ * from test where object_id=12;
Execution Plan
----------------------------------------------------------
Plan hash value: 1357081020

--------------------------------------------------------------------------
| Id  | Operation	  | Name | Rows  | Bytes | Cost (%CPU)| Time	 |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |	 |     1 |   132 |   398   (1)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| TEST |     1 |   132 |   398   (1)| 00:00:01 |
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("OBJECT_ID"=12)

Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 1 (N - Unresolved (1))
---------------------------------------------------------------------------

   1 -	SEL$1
	 N -  index(text, idx_test_id)


Statistics
----------------------------------------------------------
	  3  recursive calls
	  6  db block gets
       1433  consistent gets
	  0  physical reads
       1396  redo size
       2686  bytes sent via SQL*Net to client
	431  bytes received via SQL*Net from client
	  2  SQL*Net roundtrips to/from client
	  0  sorts (memory)
	  0  sorts (disk)
	  1  rows processed

可以看到,收集完统计信息后,就算 hint 强制走索引也不生效

设置参数optimizer_use_invisible_indexes为true后,此时优化器就会走索引范围扫描了。

SQL> alter session set optimizer_use_invisible_indexes=true;
Session altered.

SQL> select * from test where object_id=12;
Execution Plan
----------------------------------------------------------
Plan hash value: 2500689602

---------------------------------------------------------------------------------------------------
| Id  | Operation			    | Name	  | Rows  | Bytes | Cost (%CPU)| Time	  |
---------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT		    |		  |	1 |   132 |	2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| TEST	  |	1 |   132 |	2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN		    | IDX_TEST_ID |	1 |	  |	1   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("OBJECT_ID"=12)


Statistics
----------------------------------------------------------
	  2  recursive calls
	  6  db block gets
	  5  consistent gets
	  0  physical reads
       1428  redo size
       2686  bytes sent via SQL*Net to client
	399  bytes received via SQL*Net from client
	  2  SQL*Net roundtrips to/from client
	  0  sorts (memory)
	  0  sorts (disk)
	  1  rows processed

参考文档:https://www.cnblogs.com/kerrycode/p/7545448.html