關於虛擬索引的學習(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.
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)