1. 程式人生 > 其它 >關於不可見索引的學習(r3筆記74天)

關於不可見索引的學習(r3筆記74天)

不可見索引在日常工作中可能實用比較少,自己體驗了一把,還是比較實用的功能,在平時的工作中不妨嘗試一下。 我們來先體驗一下,然後再細細的總結一下。 測試環境基於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標明索引是否可見。

SQL> 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