After invisible indexes got introduced in 11g, they have now been enhanced in 12c: You can have multiple indexes on the same set of columns with that feature. Why would you want to use that? Actually, this is always the first question I ask when I see a new feature – sometimes it’s really hard to answer 🙂
Here, a plausible use case could be that you expect a new index on the same column to be an improvement over the existing old index, but you are not 100% sure. So instead of just dropping the old index, you make it invisible first to see the outcome:
[oracle@uhesse ~]$ sqlplus adam/adam SQL*Plus: Release 12.1.0.2.0 Production on Tue Jul 28 08:11:16 2015 Copyright (c) 1982, 2014, Oracle. All rights reserved. Last Successful login time: Tue Jul 28 2015 08:00:34 +02:00 Connected to: Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production With the Partitioning, OLAP, Advanced Analytics and Real Application Testing options SQL> col index_name for a10 SQL> select index_name,index_type,visibility from user_indexes; INDEX_NAME INDEX_TYPE VISIBILIT ---------- --------------------------- --------- BSTAR NORMAL VISIBLE SQL> col segment_name for a10 SQL> select segment_name,bytes/1024/1024 from user_segments; SEGMENT_NA BYTES/1024/1024 ---------- --------------- BSTAR 160 SALES 600 SQL> set timing on SQL> select count(*) from sales where channel_id=3; COUNT(*) ---------- 2000000 Elapsed: 00:00:00.18 SQL> set timing off SQL> @lastplan PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- SQL_ID b7cvb9nu10qdb, child number 0 ------------------------------------- select count(*) from sales where channel_id=3 Plan hash value: 2525234362 --------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 3872 (100)| | | 1 | SORT AGGREGATE | | 1 | 3 | | | PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- |* 2 | INDEX RANGE SCAN| BSTAR | 2000K| 5859K| 3872 (1)| 00:00:01 | --------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("CHANNEL_ID"=3) 19 rows selected.
So I have an ordinary B* index here that supports my query, but I suspect that it would work better with a bitmap index. In older versions, you would get this if you try to create it with the old index still existing:
SQL> create bitmap index bmap on sales(channel_id) nologging;
create bitmap index bmap on sales(channel_id) nologging
*
ERROR at line 1:
ORA-01408: such column list already indexed
Enter the 12c New Feature:
SQL> alter index bstar invisible; Index altered. SQL> create bitmap index bmap on sales(channel_id) nologging; Index created.
Now I can check if the new index is really an improvement while the old index remains in place and is still being maintained by the system. So in case the new index turns out to be a bad idea – no problem to fall back on the old one!
SQL> select index_name,index_type,visibility from user_indexes; INDEX_NAME INDEX_TYPE VISIBILIT ---------- --------------------------- --------- BMAP BITMAP VISIBLE BSTAR NORMAL INVISIBLE SQL> select segment_name,bytes/1024/1024 from user_segments; SEGMENT_NA BYTES/1024/1024 ---------- --------------- BMAP 9 BSTAR 160 SALES 600 SQL> set timing on SQL> select count(*) from sales where channel_id=3; COUNT(*) ---------- 2000000 Elapsed: 00:00:00.01 SQL> @lastplan PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------ SQL_ID b7cvb9nu10qdb, child number 0 ------------------------------------------------------------------------------------ select count(*) from sales where channel_id=3 Plan hash value: 3722975061 ------------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | | | 216 (100)| | | 1 | SORT AGGREGATE | | 1 | 3 | | | PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------ | 2 | BITMAP CONVERSION COUNT | | 2000K| 5859K| 216 (0)| 00:00:01 | |* 3 | BITMAP INDEX SINGLE VALUE| BMAP | | | | | ------------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 3 - access("CHANNEL_ID"=3) 20 rows selected.
Looks like everything is better with the new index, right? Let’s see what the optimizer thinks about it:
SQL> alter index bmap invisible; Index altered. SQL> select index_name,index_type,visibility from user_indexes; INDEX_NAME INDEX_TYPE VISIBILIT ---------- --------------------------- --------- BMAP BITMAP INVISIBLE BSTAR NORMAL INVISIBLE SQL> alter session set optimizer_use_invisible_indexes=true; Session altered. SQL> select count(*) from sales where channel_id=3; COUNT(*) ---------- 2000000 SQL> @lastplan PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------- SQL_ID b7cvb9nu10qdb, child number 0 ------------------------------------------------------------------------------------- select count(*) from sales where channel_id=3 Plan hash value: 3722975061 ------------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | | | 216 (100)| | | 1 | SORT AGGREGATE | | 1 | 3 | | | PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------ | 2 | BITMAP CONVERSION COUNT | | 2000K| 5859K| 216 (0)| 00:00:01 | |* 3 | BITMAP INDEX SINGLE VALUE| BMAP | | | | | ------------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 3 - access("CHANNEL_ID"=3) 20 rows selected.
The optimizer agrees that the new index is better. I could keep both indexes here in place, but remember that the old index still consumes space and requires internal maintenance. Therefore, I decide to drop the old index:
SQL> drop index bstar; Index dropped. SQL> alter index bmap visible; Index altered.
Hope that helped to answer the question why you would want to use that 12c New Feature. As always:
#1 von Rodrigo Jorge am Juli 30, 2015 - 02:50
Very good post.
#2 von Oren Nakdimon (@DBoriented) am August 3, 2015 - 14:34
Hi Uwe.
Just a small suggestion regarding the following:
> SQL> alter index bstar invisible;
>
> Index altered.
>
> SQL> create bitmap index bmap on sales(channel_id) nologging;
>
> Index created.
If the table is big, then the creation of the new index may take some time, during which there is no visible index to support the queries.
It will be safer to do it in a different order, like this:
SQL> create bitmap index bmap on sales(channel_id) nologging INVISIBLE;
Index created.
SQL> alter index bstar INVISIBLE;
Index altered.
SQL> alter index bmap VISIBLE;
Index altered.
So the time with no visible supporting index is independent of the time needed for creating the new index, and becomes almost zero, especially with one more enhancement in Oracle 12c – ALTER INDEX VISIBLE and ALTER INDEX INVISIBLE are online operations (they were offline in 11g).
Thanks,
Oren.
#3 von Uwe Hesse am August 4, 2015 - 05:06
Oren, thank you for the comment! Yes, that sounds like a good idea 🙂