關於不可見索引的學習

jeanron100發表於2014-12-04
不可見索引在日常工作中可能使用比較少,自己體驗了一把,還是比較實用的功能,在平時的工作中不妨嘗試一下。
我們來先體驗一下不可見索引,然後再總結一下。
測試環境基於11gR2
> sqlplus -v
SQL*Plus: Release 11.2.0.2.0 Production

我們先來建立一個表來看看,然後建立一個不可見索引,來看看索引的訪問情況。
create table t as select *from dba_objects where object_id is not null and rownum<100000;
我們隨機抽取5條資料,後續的查詢都會基於裡面的1條資料來做說明。
select *from (select object_id from t  order by dbms_random.value()) where rownum<5;
 OBJECT_ID
----------
      9445
      4672
      5048
      4096
開啟debug,來看看sql語句的執行情況。
set autot trace exp stat
我們選取了object_id為9445的記錄。在沒有索引的情況下,看看執行情況,好做比對。
select *from t where object_id=9445 ;
Execution Plan
----------------------------------------------------------
Plan hash value: 1601196873
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |   116 | 24012 |    26   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| T    |   116 | 24012 |    26   (0)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("OBJECT_ID"=9445)
Statistics
----------------------------------------------------------
         18  recursive calls
          0  db block gets
        177  consistent gets
          0  physical reads
          0  redo size
       1632  bytes sent via SQL*Net to client
        520  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

上面的語句毫無疑問走了全表掃描,因為我們還沒有建立索引。

我們來建立一個唯一性索引,來在查詢中啟用一下索引,這個時候索引還是可見的。
create unique index inx_t on t(object_id) ;

SQL> select *from t where object_id=9445;
Execution Plan
----------------------------------------------------------
Plan hash value: 1855406669
-------------------------------------------------------------------------------------
| Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |       |     1 |   207 |     1   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T     |     1 |   207 |     1   (0)| 00:00:01 |
|*  2 |   INDEX UNIQUE SCAN         | INX_T |     1 |       |     1   (0)| 00:00:01 |
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("OBJECT_ID"=9445)
Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
          3  consistent gets
          1  physical reads
          0  redo size
       1499  bytes sent via SQL*Net to client
        509  bytes received via SQL*Net from client
          1  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed
這個時候一切都沒有什麼特別之處。
我們來嘗試下面的步驟。把索引改為不可見索引,或者我們刪除已經存在的索引,然後重建一個不可見索引。
--刪除,重建索引
drop index inx_t;
create unique index inx_t on t(object_id) invisible;
--修改索引屬性,改為不可見
alter index inx_t invisible;

然後再次嘗試執行同樣的sql語句。看看是否索引能夠正常啟用。
select *from t where object_id=9445;
Execution Plan
----------------------------------------------------------
Plan hash value: 1601196873
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |   116 | 24012 |    26   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| T    |   116 | 24012 |    26   (0)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("OBJECT_ID"=9445)
Statistics
----------------------------------------------------------
          9  recursive calls
          0  db block gets
        139  consistent gets
          0  physical reads
          0  redo size
       1632  bytes sent via SQL*Net to client
        520  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed
這個時候索引沒有啟用,感覺就跟刪除了一樣。其實在dml操作中還是會有消耗和正常的索引是一樣的,只是在最佳化器中對這個索引不可見。
我們來使用hint看看是否能夠正常啟用。
select /*+index(t inx_t)*/ *from t where object_id=9445;
SQL> select /*+index(t inx_t)*/ *from t where object_id=9445;
Execution Plan
----------------------------------------------------------
Plan hash value: 1601196873
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |   116 | 24012 |    26   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| T    |   116 | 24012 |    26   (0)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("OBJECT_ID"=9445)
Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
        135  consistent gets
          0  physical reads
          0  redo size
       1632  bytes sent via SQL*Net to client
        520  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,預設是不會啟用不可見索引的。我們可以從session級別,system級別進行設定。一般都會在session級啟用,來檢視索引是否能夠達到預期目標,然後再決定是否設定為可見。
SQL> show parameter vis
NAME                                 TYPE                              VALUE
------------------------------------ --------------------------------- ------------------------------
optimizer_use_invisible_indexes      boolean                           FALSE

我們在session級啟用。
alter session set "optimizer_use_invisible_indexes"=true;
然後執行同樣的語句。可以看到索引能夠正常啟用了。
select *from t where object_id=9445;
Execution Plan
----------------------------------------------------------
Plan hash value: 1855406669
-------------------------------------------------------------------------------------
| Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |       |     1 |   207 |     1   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T     |     1 |   207 |     1   (0)| 00:00:01 |
|*  2 |   INDEX UNIQUE SCAN         | INX_T |     1 |       |     1   (0)| 00:00:01 |
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("OBJECT_ID"=9445)
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          3  consistent gets
          0  physical reads
          0  redo size
       1499  bytes sent via SQL*Net to client
        509  bytes received via SQL*Net from client
          1  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

我們恢復原值。來看看,又開始走了全表掃描。
alter session set "optimizer_use_invisible_indexes"=false;

select *from t where object_id=9445;
Execution Plan
----------------------------------------------------------
Plan hash value: 1601196873
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |   116 | 24012 |    26   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| T    |   116 | 24012 |    26   (0)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("OBJECT_ID"=9445)
Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
        135  consistent gets
          0  physical reads
          0  redo size
       1632  bytes sent via SQL*Net to client
        520  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

如果在session級別確認了索引能夠極大的提高效率,可以修改索引的屬性,把索引置為可見狀態。
我們設定索引為可見,看看語句的執行情況,索引又能夠正常啟用了。
alter index inx_t visible;
select *from t where object_id=9445;
Execution Plan
----------------------------------------------------------
Plan hash value: 1855406669
-------------------------------------------------------------------------------------
| Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |       |     1 |   207 |     1   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T     |     1 |   207 |     1   (0)| 00:00:01 |
|*  2 |   INDEX UNIQUE SCAN         | INX_T |     1 |       |     1   (0)| 00:00:01 |
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("OBJECT_ID"=9445)
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          3  consistent gets
          0  physical reads
          0  redo size
       1499  bytes sent via SQL*Net to client
        509  bytes received via SQL*Net from client
          1  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

大體的測試就這麼多,還有幾個問題需要考慮一下,如果不可見索引的使用這麼靈活。我們可以根據不可見索引來評估一條sql語句的執行效率情況,那麼是否在高可用場景中使用呢。
我們可以做一個簡單的測試來說明,在建立不可見索引的時候,是否還存在著鎖,這樣就能夠判斷是否適用於高可用場景了。
我們刪除表,然後建立新的表,表中的資料儘量多一些,讓建立索引的時間稍微長一點。
drop table t;
create table t as select *from dba_objects where rownum<5000000;
create  index inx_t on t(object_id) invisible;

然後我們檢視鎖的情況。可以看到在建立不可見索引的過程中還是存在著鎖。線上業務中還是會有影響的。
SID_SERIAL   ORACLE_USE OBJECT_NAME     LOGON_TIM SEC_WAIT OSUSER     MACHINE    PROGRAM                 STATE              STATUS     LOCK_ MODE_HELD
------------ ---------- --------------- --------- -------- ---------- ---------- -------------------- ---------- ---------- ----- ----------
763,33293    N1         OBJ$            04-DEC-14        0 testuser   testdb    sqlplus@xx (TNS V1-V3)  WAITED SHORT TIME   ACTIVE     DML   Row-X (SX)
763,33293    N1         T               04-DEC-14        0 testuser   testdb    sqlplus@xx (TNS V1-V3)  WAITED SHORT TIME   ACTIVE     DML   Share
763,33293    N1         T               04-DEC-14        0 testuser    testdb   sqlplus@xx (TNS V1-V3)  WAITED SHORT TIME   ACTIVE     DL    Row-X (SX)
763,33293    N1         T               04-DEC-14        0 testuser    testdb   sqlplus@xx (TNS V1-V3)  WAITED SHORT TIME   ACTIVE     DL    Row-X (SX)

不可見索引在資料字典檢視中和普通索引沒有太大的區別,都有對應的索引段,這是在索引對應的資料字典中有一個欄位visibility標明索引是否可見。
 select segment_name,segment_type,blocks from user_segments where segment_name='INX_T'
SEGMENT_NAME         SEGMENT_TYPE           BLOCKS
-------------------- ------------------ ----------
INX_T                INDEX                   10240

SQL> SELECT INDEX_NAME,VISIBILITY FROM USER_INDEXES WHERE INDEX_NAME='INX_T';
INDEX_NAME                     VISIBILIT
------------------------------ ---------
INX_T                          INVISIBLE


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/23718752/viewspace-1356820/,如需轉載,請註明出處,否則將追究法律責任。

相關文章