1. 程式人生 > 其它 >關於虛擬索引的學習(r3筆記第75天)

關於虛擬索引的學習(r3筆記第75天)

昨天簡單總結了下不可見索引,今天來說說虛擬索引。 這兩個索引聽起來有點類似。其實差別還是比較大。 不可見索引有對應的索引段,而虛擬索引沒有對應的索引段存在。 不可見索引可以通過alter語句來直接切換可見不可見。而對於虛擬索引而言這些操作都不支援。 不可見索引可以在user_indexes中查到對應的資料字典資訊。但是虛擬索引在user_indexes中都沒有記錄,最後只能從dba_objects裡面勉強查到一條它存在的記錄。 不可見索引和虛擬索引都有對應的資料庫引數,可以通過alter session,system來修改生效。 不可見索引在優化器中進行了遮蔽,使得索引的可見/不可見都可以靈活的切換,而虛擬索引是在希望在優化器中做標識,使得語句的執行計劃能夠考慮到對應的虛擬索引的作用。 個人覺得,在日常的使用中對於索引建立存在爭議的場景中,可以考慮使用虛擬索引,來通過檢視執行計劃來比較前後的變化。如果提升的幅度很大,再考慮建立對應的索引。 我們來舉個簡單的例子來說明一下虛擬索引。 我們來建立一個表,然後首先驗證建立一個普通索引驗證索引能夠正常啟用,然後刪除索引,建立虛擬索引來看看語句的執行情況。 建立表t SQL> create table t as select *from dba_objects where object_id is not null and rownum<100000; Table created.

然後隨機抽取4條資料。 SQL> select *from (select object_id from t order by dbms_random.value()) where rownum<5; OBJECT_ID ---------- 22969 20703 13851 8040 SQL> set autot trace exp stat 我們來看看當前的執行計劃,因為沒有索引,索引會走全表掃描。


SQL> select *from t where object_id=8040;
Execution Plan
----------------------------------------------------------
Plan hash value: 1601196873
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |  1233 |   249K|   267   (2)| 00:00:04 |
|*  1 |  TABLE ACCESS FULL| T    |  1233 |   249K|   267   (2)| 00:00:04 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("OBJECT_ID"=8040)
Statistics
----------------------------------------------------------
         18  recursive calls
          0  db block gets
       1525  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

我們建立對應的索引。來看看語句的執行情況。


SQL> create unique index inx_t on t(object_id) ;
Index created.
SQL> select *from t where object_id=8040;
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"=8040)
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

刪除索引,建立一個虛擬索引。來對比一下執行計劃的情況。

SQL> drop index inx_t;
Index dropped.
SQL> create unique index inx_t on t(object_id) nosegment;
Index created.
SQL> select *from t where object_id=8040;
Execution Plan
----------------------------------------------------------
Plan hash value: 1601196873
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |  1233 |   249K|   267   (2)| 00:00:04 |
|*  1 |  TABLE ACCESS FULL| T    |  1233 |   249K|   267   (2)| 00:00:04 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("OBJECT_ID"=8040)
Statistics
----------------------------------------------------------
          9  recursive calls
          0  db block gets
       1487  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是否有作用。


SQL> select /*+index(t inx_t)*/ *from t where object_id=8040;
Execution Plan
----------------------------------------------------------
Plan hash value: 1601196873
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |  1233 |   249K|   267   (2)| 00:00:04 |
|*  1 |  TABLE ACCESS FULL| T    |  1233 |   249K|   267   (2)| 00:00:04 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("OBJECT_ID"=8040)
Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
       1483  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

這個時候,我們得知虛擬索引沒有啟用,至於啟用的方法,就是通過一個數據庫引數_use_nosegment_indexes來實現。可以在session,system級別做設定。一般來說我們在session級做簡單的對比測試,如果執行計劃的效果提升很多,然後可以根據情況再建立存在段的索引。或者在系統級開啟這個開關。 我們先在session級別開啟。

SQL> alter session set "_USE_NOSEGMENT_INDEXES" = true;
Session altered.

我們來檢視一下語句的執行情況。

SQL> select *from t where object_id=8040;
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"=8040)
Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
       1483  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

可以看到,索引被啟用了。其實這個過程中的資源消耗很低,因為沒有對應的索引段存在,完全是根據優化器的判斷。 我們關閉這個開關。

SQL> alter session set "_USE_NOSEGMENT_INDEXES" = false;
Session altered.

嘗試使用alter語句來rebuild這個虛擬索引。

SQL> alter index inx_t rebuild;
alter index inx_t rebuild
*
ERROR at line 1:
ORA-08114: can not alter a fake index

alter語句在虛擬索引中式不支援的。 我們來查查資料字典裡的資訊。

SQL> set autot off
SQL>  select segment_name,segment_type,blocks from user_segments where segment_name='INX_T';
no rows selected
SQL> select index_name ,dropped ,segment_created from user_indexes where index_name='INX_T';
no rows selected
在user_segments,user_indexes中都沒有對應的記錄存在,我都懷疑索引是否存在。
SQL> create unique index inx_t on t(object_id) nosegment;
create unique index inx_t on t(object_id) nosegment
                    *
ERROR at line 1:
ORA-00955: name is already used by an existing object

最後在dba_objects裡面終於找到一條記錄。

SQL> select object_name, object_type from dba_objects where object_name = 'INX_T';
OBJECT_NAME                                                                                                                      OBJECT_TYPE
-------------------------------------------------------------------------------------------------------------------------------- -------------------
INX_T                                                                                                                            INDEX

關於虛擬索引,可以在metalink上參考Virtual Indexes (Doc ID 1401046.1)