Wednesday, February 21, 2018

Virtual index (fake index)

At my work got few questions about Virtual index from fellow DBA, how about creating virtual index on a live production system and having them tested against the production workloads.
Before getting into those response in detail, thought it worth a blog post to help explain about virtual index.
It is possible to create Virtual index (aka fake index) manually using NOSEGMENT clause, kind of “pretend” to see what the CBO might do if such an index really exists.
Creating a virtual index doesn’t consume any storage and doesn’t maintained any way during the DML operations on the table, and it can’t be altered or rebuild like the real index. You can do stats gather (using dbms_stats) or analyze validate structure but no details will be available in user_indexes and index_stats data dictionary.
It is only visible to the CBO if and only if the session has set this hidden parameter (_use_nosegment_indexes = true)
The CBO will now consider to cost the index and potentially include them in the explain plan, however at runtime Oracle can of course not use that index and revert to the next best thing.
Let’s start with a demo. With a table in which the column OBJECT_ID values are distributed throughout the whole table.
demo@ORA12C> create table t( id int, object_id int, created date );
 
Table created.
 
demo@ORA12C> insert into t (id, object_id, created)
  2  select id, mod(id,100)+1 , created
  3  from big_table;
 
1000000 rows created.
 
demo@ORA12C> commit;
 
Commit complete.
 
demo@ORA12C> exec dbms_stats.gather_table_stats(user,'T');
 
PL/SQL procedure successfully completed.
 
demo@ORA12C>
 
We can now create a virtual index based on the OBJECT_ID column.
demo@ORA12C> create index t_idx on t(object_id) NOSEGMENT;
 
Index created.
 
demo@ORA12C>
 
Creating a virtual index makes no entry in USER_INDEXES and USER_SEGMENTS dictionary, but mark an entry into USER_OBJECTS.
 
demo@ORA12C> select * from user_segments where segment_name ='T_IDX';
 
no rows selected
 
demo@ORA12C> select * from user_indexes where index_name ='T_IDX';
 
no rows selected
 
demo@ORA12C> select object_name,status from user_objects where object_name ='T_IDX';
 
OBJECT_NAME                    STATUS
------------------------------ -------
T_IDX                          VALID
 
demo@ORA12C>
 
 
Now to verify if the optimizer make use of the virtual index, let’s do an explain plan.
 
 
demo@ORA12C> alter session set "_use_nosegment_indexes"=true;
 
Session altered.
 
demo@ORA12C> set autotrace traceonly explain
demo@ORA12C> select * from t where object_id = 42;
 
Execution Plan
----------------------------------------------------------
Plan hash value: 767293772
 
---------------------------------------------------------------------------------------------
| Id  | Operation                           | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |       | 10000 |   156K|     9   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| T     | 10000 |   156K|     9   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN                  | T_IDX | 10000 |       |     1   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   2 - access("OBJECT_ID"=42)
 
demo@ORA12C> set autotrace off
demo@ORA12C> alter session set "_use_nosegment_indexes"=false;
 
Session altered.
 
demo@ORA12C>
 
Indeed the virtual index is costed and used in the explain plan. If we now physically create this index for real then.
 
demo@ORA12C> drop index t_idx;
 
Index dropped.
 
demo@ORA12C> create index t_idx on t(object_id);
 
Index created.
 
demo@ORA12C> column index_name format a10
demo@ORA12C> select i.index_name,i.clustering_factor,t.blocks,t.num_rows
  2  from user_indexes i,
  3       user_tables t
  4  where t.table_name = i.table_name
  5  and i.index_name ='T_IDX';
 
INDEX_NAME CLUSTERING_FACTOR     BLOCKS   NUM_ROWS
---------- ----------------- ---------- ----------
T_IDX                 288917       2890    1000000
 
demo@ORA12C>
demo@ORA12C> set autotrace traceonly explain
demo@ORA12C> select * from t where object_id = 42;
 
Execution Plan
----------------------------------------------------------
Plan hash value: 1601196873
 
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      | 10000 |   156K|   796   (2)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| T    | 10000 |   156K|   796   (2)| 00:00:01 |
--------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - filter("OBJECT_ID"=42)
 
demo@ORA12C> set autotrace off
 
we see the CBO has decided to perform the now cheaper full table scan, Although the Virtual Index on this column was used, once the actual characteristics of the index are determined via the index statistics, the CBO has decided the actual physical index was just too expensive to use to retrieve the 1% of rows.
 
Virtual index is one among many heuristics built into the CBO. Virtual index can be useful to quickly determine whether an index is a viable option if it were to be actually created, however caution need to be exercised.
 
The big advantage of a Virtual Index of course is that it doesn’t really exist and so consumes no storage and can be created extremely quickly/cheaply.
 
The disadvantage of a Virtual index is that it doesn’t really exist and so Oracle cannot collect segment level statistics. Without statistics however, the CBO has a very tough time of doing its job properly …
So don’t use virtual index in live production system, since it is an undocumented feature and also requires an undocumented parameter set at the session level.
But it is safe to play with virtual index in Pre-prod/SIT/UAT/Test database and see if the real index benefit the workload. 
 
Always have the real index in the live production system but not the virtual index.

No comments:

Post a Comment