Identifying Bloated Index in Oracle

Indexes have always been a topic of interest for DBA/Developers. When it comes to index rebuild there have been many opinions floating across internet on when to rebuild these indexes. Many do say when the BLEVEL is > 3 one should rebuild the indexes. I don’t believe in that and i think i have never seen BLEVEL > 3 for index till now.

Over a period of time, the index can get fragmented because of the DML’s occurring on the table. The free space within the block of index can get used depending on the incoming column value, maintaining the index structure (sorted).

Now, suppose you have a table with one of the column as CREATION_DATE sysdate(DEFAULT), and every night data is loaded into it and suppose as per the application logic previous date data is deleted. Now an index having creation_date column will slowly and gradually increase in size and as the left side of the index will always be empty and index keeps growing toward right side.

With this kind of indexes, the performance many degrade for sqls , the plans many flip etc. It is sometimes good to rebuild indexes.But how to identify which indexes to be rebuild, is the question.

I was working on finding which indexes are bloated and below is the sql based on few logic

WITH spv AS (select di.table_name,
spv.object_name,
di.leaf_blocks,
di.index_type,
di.num_rows,
decode(di.uniqueness,'UNIQUE',0,1)uniq_ind,
di.last_analyzed,sum(bytes),
sum(io_cost) 
from v$sql_plan spv,dba_indexes di
     where spv.object_owner = 'ANAND' and         
           spv.object_type LIKE '%INDEX%' and 
           spv.object_name=di.index_name and 
           spv.object_owner=di.owner and 
           di.leaf_blocks > 1000
     group by di.table_name,
              spv.object_name,
              di.leaf_blocks,
              di.index_type,
              di.num_rows,
              di.uniqueness,
              di.last_analyzed order by 3)
select spv.table_name,
       spv.OBJECT_NAME index_name,
       spv.leaf_blocks leaf_blocks,
       round (100 / 90 *(spv.num_rows * (tab.rowid_length + spv.uniq_ind + 4) + sum((tc.avg_col_len)*(tab.num_rows)))/(8192 - 192))target_blocks, 
       round(((((spv.LEAF_BLOCKS) - (100 / 90 *(spv.num_rows * (tab.rowid_length + spv.uniq_ind + 4) +  sum((tc.avg_col_len)*(tab.num_rows)))/(8192 - 192)))/spv.LEAF_BLOCKS)*100)) DIFF_PCT
from spv, 
     (select table_name,num_rows,decode(partitioned,'YES',10,6) rowid_length  from dba_tables where owner='ANAND') tab,
     dba_tab_cols tc,
     dba_ind_columns ic 
where
     spv.table_name=tab.table_name and
     tc.column_name = ic.column_name  and
     tab.TABLE_NAME=tc.table_name and 
     ic.TABLE_NAME=tab.table_name and 
     spv.object_name=ic.INDEX_NAME
having round(((((spv.LEAF_BLOCKS) - (100 / 90 *(spv.num_rows * (tab.rowid_length + spv.uniq_ind + 4) + sum((tc.avg_col_len)*(tab.num_rows)))/(8192 - 192)))/spv.LEAF_BLOCKS)*100)) > 70
group by spv.table_name,
         spv.object_name,
         spv.leaf_blocks,
         spv.num_rows,spv.uniq_ind, 
         tab.rowid_length 
order by 5

Output from a test db —

TABLE_NAME                     INDEX_NAME                     LEAF_BLOCKS TARGET_BLOCKS   DIFF_PCT
------------------------------ ------------------------------ ----------- ------------- ----------
COMPLETED_CUST                 I_CCS_COND                           25340          7236         71
CUST_ONE_ITEMS                 I_CSI_REQ_ID                          7999          2351         71
DEMANDS                        I_DEMAND                             26920          7478         72
.....................................
.....................................
PROP_VALUES                    PK_SID_VALUE                         11847           831         93
SLA_METRS                      PK_SLA_METCS                         12840           129         99

–> leaf_blocks is the actual leaf blocks of the index from dba_indexes.
–> Target_Blocks represents the estimated no. of leaf blocks based on the current stats on the table. So, stats needs to be latest on the table.
–> The above sql displays all the index name where the % difference between leaf_blocks and estimated target blocks is > 70
–> The index names comes from v$sql_plan as those are the indexes being used by the optimizer.
–> The indexes having leaf_blocks > 1000 are selected

Now, lets rebuild the index and see do we get any closer target_blocks

12:53:24 DBA@test:1> select leaf_blocks from dba_indexes where index_name='PK_SLA_METCS';

LEAF_BLOCKS
-----------
        12840

12:52:59 DBA@test:1> alter index xxx.PK_SLA_METCS rebuild online parallel 4;

Index altered.

Elapsed: 00:00:00.87
12:53:24 DBA@test:1> select leaf_blocks from dba_indexes where index_name='PK_SLA_METCS';

LEAF_BLOCKS
-----------
        135  <-- Estimated TARGET_BLOCK was 129 

Elapsed: 00:00:00.43

13:15:46 DBA@test:1> select leaf_blocks from dba_indexes where index_name='PK_SID_VALUE';

LEAF_BLOCKS
-----------
      11847

Elapsed: 00:00:00.45
13:15:55 DBA@test:1> alter index xxx.PK_SID_VALUE rebuild online parallel 4;


Index altered.

Elapsed: 00:00:25.25
13:16:42 DBA@test:1> select leaf_blocks from dba_indexes where index_name='PK_SID_VALUE';

LEAF_BLOCKS
-----------
        840 <-- Estimated TARGET_BLOCK was 831

 

I would say this is just the version 1.0 :)

REFERENCE –> Script to investigate a b-tree index structure (Doc ID 989186.1)

Flashback : Guaranteed Restore Point

Oracle Flashback database and restore points enables us to rewind the database back in time to correct any problems caused by logical data corruption or user errors and it doesn’t require any restoration of backup. There are 2 types of restoration points —

1. Normal Restore Point –> assigns a restore point name to an SCN or specific point in time.The control file stores the name of the restore point and the SCN.

2. Guaranteed Restore Point –> Like Normal restore point, it also serves as an alias for an SCN in recovery operation. The only difference is that the guaranteed restore points never age out of the control file and must be explicitly dropped.

Logging for Guaranteed Restore Points with Flashback Logging Disabled

Assume that you create a guaranteed restore point when logging for Flashback Database is disabled. In this case, the first time a data file block is modified after the time of the guaranteed restore point, the database stores an image of the block before the modification in the flashback logs. Thus, the flashback logs preserve the contents of every changed data block at the time that the guaranteed restore point was created. Later modifications to the same block do not cause the contents to be logged again unless another guaranteed restore point was created after the block was last modified.


11:31:49 SYS@ORCL:1> select name,database_role,open_mode,flashback_on,log_mode from v$database;

NAME      DATABASE_ROLE    OPEN_MODE            FLASHBACK_ON       LOG_MODE
--------- ---------------- -------------------- ------------------ ------------
ORCL      PRIMARY          READ WRITE           NO                 ARCHIVELOG

11:31:54 SYS@ORCL:1> show parameter recovery

NAME                           TYPE        VALUE
------------------------------ ----------- ----------------------------------------------------------------------------------------------------
db_recovery_file_dest          string      D:\oracle\flashback\orcl
db_recovery_file_dest_size     big integer 2G
recovery_parallelism           integer     0
11:31:56 SYS@ORCL:1>  SELECT NAME, SCN, TIME, DATABASE_INCARNATION#, GUARANTEE_FLASHBACK_DATABASE, STORAGE_SIZE FROM V$RESTORE_POINT WHERE GUARANTEE_FLASHBACK_DATABASE='YES';

no rows selected

Create Guaranteed restore point —

11:31:58 SYS@ORCL:1> CREATE RESTORE POINT test_anand GUARANTEE FLASHBACK DATABASE;

Restore point created.

11:32:41 SYS@ORCL:1>
11:33:27 SYS@ORCL:1>  SELECT NAME, SCN, TIME, DATABASE_INCARNATION#, GUARANTEE_FLASHBACK_DATABASE, STORAGE_SIZE FROM V$RESTORE_POINT WHERE GUARANTEE_FLASHBACK_DATABASE='YES';

NAME                SCN TIME                                DATABASE_INCARNATION# GUA STORAGE_SIZE
------------ ---------- ----------------------------------- --------------------- --- ------------
TEST_ANAND      1297697 14-JAN-13 11.32.39.000000000 AM                         2 YES     52428800

Alert log shows —


Mon Jan 14 11:32:39 2013
Starting background process RVWR
Mon Jan 14 11:32:39 2013
RVWR started with pid=25, OS id=7032 
Allocated 3981204 bytes in shared pool for flashback generation buffer
Created guaranteed restore point TEST_ANAND

Lets create an user, table and do some dmls

11:35:36 SYS@ORCL:1> create user anand identified by anand123 default tablespace users;

User created.

11:36:16 SYS@ORCL:1>
11:36:17 SYS@ORCL:1> grant connect,resource to anand;

Grant succeeded.

11:36:25 SYS@ORCL:1>
11:36:42 SYS@ORCL:1> grant dba to anand;

Grant succeeded.

11:36:48 SYS@ORCL:1> conn anand/anand123
Connected.
11:36:52 ANAND@ORCL:1>
11:36:53 ANAND@ORCL:1> create table test as select * from all_objects;

Table created.
11:37:52 ANAND@ORCL:1> insert into test select * from test;

72583 rows created.

...................
....................
....................

11:39:01 ANAND@ORCL:1> insert into test select * from test;

1161328 rows created.

11:41:11 ANAND@ORCL:1> select * from V$FLASHBACK_DATABASE_LOG;

OLDEST_FLASHBACK_SCN OLDEST_FLASHBACK_TI RETENTION_TARGET FLASHBACK_SIZE ESTIMATED_FLASHBACK_SIZE
-------------------- ------------------- ---------------- -------------- ------------------------
             1297697 2013.01.14 11:32:41             1440      104857600                        0

11:41:30 ANAND@ORCL:1>
11:41:31 ANAND@ORCL:1>
11:41:31 ANAND@ORCL:1> select * from V$FLASHBACK_DATABASE_STAT;

BEGIN_TIME          END_TIME            FLASHBACK_DATA    DB_DATA  REDO_DATA ESTIMATED_FLASHBACK_SIZE
------------------- ------------------- -------------- ---------- ---------- ------------------------
2013.01.14 11:32:41 2013.01.14 11:41:50       12484608  199311360  278596608                        0

11:41:50 ANAND@ORCL:1>
11:42:35 ANAND@ORCL:1> select * from V$FLASHBACK_DATABASE_LOGFILE;

NAME                                                               LOG#    THREAD#  SEQUENCE#      BYTES FIRST_CHANGE# FIRST_TIME          TYPE
------------------------------------------------------------ ---------- ---------- ---------- ---------- ------------- ------------------- ---------
D:\ORACLE\FLASHBACK\ORCL\ORCL\FLASHBACK\O1_MF_8H77W05C_.FLB           1          1          1   52428800       1297697 2013.01.14 11:32:41 NORMAL
D:\ORACLE\FLASHBACK\ORCL\ORCL\FLASHBACK\O1_MF_8H77W2JM_.FLB           2          1          1   52428800             0                     RESERVED


V$FLASHBACK_DATABASE_LOG –> displays information about the flashback data. Use this view to help estimate the amount of flashback space required for the current workload.

V$FLASHBACK_DATABASE_STAT displays statistics for monitoring the I/O overhead of logging flashback data.

11:43:32 ANAND@ORCL:1> insert into test select * from test;

2322656 rows created.

11:43:47 ANAND@ORCL:1> commit;

Commit complete.

11:44:47 ANAND@ORCL:1> select * from V$FLASHBACK_DATABASE_STAT;

BEGIN_TIME          END_TIME            FLASHBACK_DATA    DB_DATA  REDO_DATA ESTIMATED_FLASHBACK_SIZE
------------------- ------------------- -------------- ---------- ---------- ------------------------
2013.01.14 11:32:41 2013.01.14 11:44:57       26501120  482787328  558669824                        0

11:44:57 ANAND@ORCL:1>

Now, lets try to flashback the database to restore point

11:52:24 SYS@ORCL:1> flashback database to restore point test_anand;
flashback database to restore point test_anand
*
ERROR at line 1:
ORA-38757: Database must be mounted and not open to FLASHBACK.

To flashback the database must be in mount mode. Shutdown the db and mount it. Before mounting the database, moved all the archive logs generated from creating the restore point till the shutdown and tried flashback.

11:54:44 SYS@ORCL:1> flashback database to restore point test_anand;
flashback database to restore point test_anand
*
ERROR at line 1:
ORA-38754: FLASHBACK DATABASE not started; required redo log is not available
ORA-38762: redo logs needed for SCN 1297662 to SCN 1297697
ORA-38761: redo log sequence 13 in thread 1, incarnation 2 could not be accessed

13:28:36 SYS@ORCL:1> select CHECKPOINT_CHANGE#,CHECKPOINT_TIME,UNRECOVERABLE_CHANGE#,UNRECOVERABLE_TIME from v$datafile;

CHECKPOINT_CHANGE# CHECKPOINT_TIME     UNRECOVERABLE_CHANGE# UNRECOVERABLE_TIME
------------------ ------------------- --------------------- -------------------
           1309113 2013.01.14 11:52:35                     0
           1309113 2013.01.14 11:52:35                     0
           1309113 2013.01.14 11:52:35                     0
           1309113 2013.01.14 11:52:35                     0
           1309113 2013.01.14 11:52:35                     0

13:29:21 SYS@ORCL:1>

Moved the archive log seq 13 (which was the seq# when guaranteed restore point was created)to the archive log destination and tried flashback

13:57:35 SYS@ORCL:1>  flashback database to restore point test_anand;

Flashback complete.

13:58:02 SYS@ORCL:1>

Alert log shows —

Mon Jan 14 13:57:54 2013
 flashback database to restore point test_anand
Flashback Restore Start
Flashback Restore Complete
Flashback Media Recovery Start
 started logmerger process
Parallel Media Recovery started with 4 slaves
Flashback Media Recovery Log D:\ORACLE\ARCHIVE\ORCL\ORCL_0001_0000000013_0804355822
Mon Jan 14 13:58:02 2013
Incomplete Recovery applied until change 1297698 time 01/14/2013 11:32:41
Flashback Media Recovery Complete
Completed: flashback database to restore point test_anand
13:58:46 SYS@ORCL:1> select * from V$FLASHBACK_DATABASE_LOG;

OLDEST_FLASHBACK_SCN OLDEST_FLASHBACK_ RETENTION_TARGET FLASHBACK_SIZE ESTIMATED_FLASHBACK_SIZE
-------------------- ----------------- ---------------- -------------- ------------------------
             1297697 14-01-13 11:32:41             1440      104857600                   172032

13:59:07 SYS@ORCL:1> select * from V$FLASHBACK_DATABASE_STAT;

BEGIN_TIME        END_TIME          FLASHBACK_DATA    DB_DATA  REDO_DATA ESTIMATED_FLASHBACK_SIZE
----------------- ----------------- -------------- ---------- ---------- ------------------------
14-01-13 11:53:38 14-01-13 13:59:10          16384   25108480          0                        0

14:00:19 SYS@ORCL:1> select CHECKPOINT_CHANGE#,CHECKPOINT_TIME,UNRECOVERABLE_CHANGE#,UNRECOVERABLE_TIME from v$datafile;

CHECKPOINT_CHANGE# CHECKPOINT_TIME     UNRECOVERABLE_CHANGE# UNRECOVERABLE_TIME
------------------ ------------------- --------------------- -------------------
           1297698 2013.01.14 11:32:41                     0
           1297698 2013.01.14 11:32:41                     0
           1297698 2013.01.14 11:32:41                     0
           1297698 2013.01.14 11:32:41                     0
           1297698 2013.01.14 11:32:41                     0

15:11:28 SYS@ORCL:1> alter database open resetlogs;

Database altered.

15:11:58 SYS@ORCL:1>
15:12:06 SYS@ORCL:1> select username,account_status,default_tablespace,profile from dba_users where username='ANAND';

no rows selected

ORA-38500: Unsupported operation: Oracle XML DB not present

While trying to import using impdp got the below error

Table "ANAND"."TEST" exists and has been truncated. Data will be loaded but all dependent metadata will be skipped due to table_exists_action of truncate
Processing object type TABLE_EXPORT/TABLE/TABLE_DATA
ORA-31693: Table data object "ANAND"."TEST":"Y2012_Q2_M06" failed to load/unload and is being skipped due to error:
ORA-38500: Unsupported operation: Oracle XML DB not present
..............

As it said XML db nor present, check for XDB status in dba_registry


COMP_NAME                            VERSION      STATUS
------------------------------------ ------------ --------
Oracle XML Database                  11.2.0.3.0   VALID

03:15:37 SYS > select owner, object_name, object_type, status from dba_objects where status = 'INVALID' and owner = 'XDB';

no rows selected

As per DOC ID 1424643.1, the error can be generated when tables metadata of the exported table in the dumpfile and the existing table at the target DB are different.On checking the table structure there wasn’t any difference.

After spending some more time on it, finally decided to deinstall/install XDB.

Deinstall -- @?/rdbms/admin/catnoqm.sql
Install --@?/rdbms/admin/catqm.sql {XDB pwd} {XDB default tbs} {XDB temporary tbs} {SecureFiles = YES/NO}

For more details one can refer to Doc id 1292089.1

After this, impdp completed successfully. :)

DBCA failing with “Diskgroup XXX is not compatible for database usage”

Today a friend of mine pinged me an error while he was trying to create database using DBCA from 11.2.0.3 RDBMS home on an exadata box.Screenshot of the error is below

dbca_error

The error was easy to understand as compatible parameter set in the database initialization parameter was lower than the compatible.rdbms set for the diskgroup in ASM. The db compatible parameter was set to 11.2.0.0.0 whereas for the diskgroup compatible.rdbms was set to 11.2.0.2.0.

So checking the ASM instance showed

SYS@+ASM7 > col COMPATIBILITY form a10
SYS@+ASM7 > col DATABASE_COMPATIBILITY form a10
SYS@+ASM7 > col NAME form a20
SYS@+ASM7 > select group_number, name, compatibility, database_compatibility from v$asm_diskgroup;

GROUP_NUMBER NAME               COMPATIBIL DATABASE_C
------------ ------------------ ---------- ----------
           1 DATA_O1            11.2.0.2.0 11.2.0.2.0
           2 RECO_O1            11.2.0.2.0 11.2.0.2.0

The compatible.asm diskgroup attribute controls the format of the ASM diskgroup metadata. Only ASM instances with a software version equal to or greater than compatible.asm can mount the diskgroup.

The compatible.rdbms diskgroup attribute determines the format of ASM files themselves. The diskgroup can be accessed by any database instance with a compatible init.ora parameter set equal to or higher than the compatible.rdbms attribute.

DBCA (choose General Purpose) doesn’t provide any screen wherein we can change the parameter value. But as we want to create the database using dbca, we need to change the parameter value in the template stored in ORACLE_HOME/assistants/dbca/templates


xxxxxx: (test1) /u01/oraadmin/test/admin> cd $ORACLE_HOME
xxxxxx: (test1) /u01/app/oracle/product/11.2.0.3/dbhome_1> cd assistants/dbca/templates/
xxxxxx: (test1) /u01/app/oracle/product/11.2.0.3/dbhome_1/assistants/dbca/templates> ls -lrt
total 292272
-rwxrw-r-- 1 oracle oinstall        83 Oct  4 07:25 create_bct.sql
-rwxrw-r-- 1 oracle oinstall       718 Oct  4 07:25 crt_cluster_interconnect.sql
-rwxrw-r-- 1 oracle oinstall      5104 Oct  4 07:25 Data_Warehouse.dbc
-rwxrw-r-- 1 oracle oinstall       122 Oct  4 07:25 drop_cluster_interconnect.sql
-rwxrw-r-- 1 oracle oinstall     13756 Oct  4 07:25 dw_x2_2.dbt
-rwxrw-r-- 1 oracle oinstall       178 Oct  4 07:25 exadata_miscellaneous.sql
-rwxrw-r-- 1 oracle oinstall   1507328 Oct  4 07:25 example.dmp
-rwxrw-r-- 1 oracle oinstall  21889024 Oct  4 07:25 example01.dfb
-rwxrw-r-- 1 oracle oinstall      4984 Oct  4 07:25 General_Purpose.dbc
-rwxrw-r-- 1 oracle oinstall       803 Oct  4 07:25 logs.sql
-rwxrw-r-- 1 oracle oinstall      4104 Oct  4 07:25 logs.wk1
-rwxrw-r-- 1 oracle oinstall       320 Oct  4 07:25 logs_to_add.lst
-rwxrw-r-- 1 oracle oinstall       311 Oct  4 07:25 logs_to_add.sql
-rwxrw-r-- 1 oracle oinstall      8208 Oct  4 07:25 logs_to_add.wk1
-rwxrw-r-- 1 oracle oinstall     11489 Oct  4 07:25 New_Database.dbt
-rwxrw-r-- 1 oracle oinstall     13558 Oct  4 07:25 oltp_x2_2.dbt
-rwxrw-r-- 1 oracle oinstall       369 Oct  4 07:25 recreate_temp.sql
-rwxrw-r-- 1 oracle oinstall   9748480 Oct  4 07:26 Seed_Database.ctl
-rwxrw-r-- 1 oracle oinstall 265691136 Oct  4 07:26 Seed_Database.dfb
-rwxrw-r-- 1 oracle oinstall       761 Oct  4 07:26 set_cluster_interconnect.sql
-rwxrw-r-- 1 oracle oinstall        18 Oct  4 07:26 set_fra_size.lst
-rwxrw-r-- 1 oracle oinstall       806 Oct  4 07:26 set_fra_size.sql
-rwxrw-r-- 1 oracle oinstall       513 Oct  4 07:26 set_fra_size.wk1
-rwxrw-r-- 1 oracle oinstall       199 Oct  4 07:26 set_use_large_pages_false.sql
xxxxxx: (test1) /u01/app/oracle/product/11.2.0.3/dbhome_1/assistants/dbca/templates>

As we choose the General Purpose template, we need to edit the value in General_Purpose.dbc and search of compatible parameter and you would see

..............
  initParam name="compatible" value="11.2.0.0.0"/
..............
..............

Edit the value to either equal to compatible.rdbms of diskgroup or higher. In our case, we set it “11.2.0.3” and dbca didn’t threw error the next time :)

To know more on ASM diskgroup compatibility read

http://www.pythian.com/news/1078/oracle-11g-asm-diskgroup-compatibility/