Monitoring Index Usage

Doug's Oracle Blog

  • Home
  • Papers
  • Books
  • C.V.
  • Fun
  • Blog

Jul 21: Monitoring Index Usage

A common requirement cropped up this week. A new Data Warehouse has just gone live and is still in the 'do we have the right indexes here' phase. In this case, the suspicion is that there are too many indexes in a specific schema and that a number of them may just be a waste of space and a performance overhead. The developers would like to know which indexes are not being used.

Oracle's index monitoring facility can be used for this, as described in Tim Hall's article. There's plenty of information that you can read through by googling Oracle Index Monitoring and that was just one of the first examples I found.

However, there are a few problems with this approach.

1) As mentioned in Tim's article, you need to have the facility enabled for long enough to be sure that indexes aren't used. Even if you leave it enabled for a month or so, who is to say that a particular index isn't vital to the performance of a year-end routine? Perhaps unlikely, but it would be a shame to find out after you've dropped the index.

2) Index monitoring simply isn't granular enough. All it will tell me is that an index has been used but I have no idea of how often it's been used. I suppose you could get round this by switching index monitoring on and off frequently and seeing which indexes only appear to be used occasionally.

3) Related to the low granularity, an index may not be used by your application at all but a DBA or developer happens to run a one-off query, which happens to use the index. I know that an index was used, but how do I know whether that use was significant or just an anomaly? The information 'YES' doesn't contain much value.

There are other approaches. Maybe we could look at V$SQL_PLAN and V$SQL_PLAN_STATISTICS or use a login trigger to capture Stored Outlines?

One approach that sprung to mind for me was something that I heard Jonathan Lewis mention at the UKOUG Northern Server Tech day. One of the things I like about Jonathan's presentations is that there are always useful nuggets of information scattered through the main narrative.

He mentioned an event that could be set to capture all the execution plans into a trace file - event 10132 - dump plan after compilation. I searched for information on this event on Metalink and the wider web and there isn't too much kicking around, so I thought I'd mention it here. Eventually we're going to set the event at the instance level, so that we can capture all of the plans and look for indexes that don't appear in the trace file. Before we do that, I have an SR open with Oracle Support to make sure that they're happy with us setting this event. For the example here, I'll just set it at the session level and executed a single new statement.

livdwhtst01:[oracle]:sqlplus testuser
SQL*Plus: Release 10.2.0.1.0 - Production on Fri Jul 21 12:12:42 2006
Copyright (c) 1982, 2005, Oracle. All rights reserved.

Enter password:

Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - 64bit Production
With the Partitioning and Data Mining options

SQL> create table test as select * from dba_objects;

Table created.

SQL> create unique index test_ix on test(object_id);

Index created.

SQL> exec dbms_stats.gather_table_stats('TESTUSER', 'TEST');

PL/SQL procedure successfully completed.

SQL> alter session set events '10132 trace name context forever, level 1';

Session altered.

SQL> select count(*) from test;

COUNT(*)
----------
106242

SQL> show parameter user_dump_dest

NAME TYPE
------------------------------------ --------------------------------
VALUE
------------------------------
user_dump_dest string
/ora/admin/MIDDWHT3/udump
SQL> exit
Disconnected from Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - 64bit Production
With the Partitioning and Data Mining options
The trace file will be in user_dump_dest for this session-level example. Let's take a look.
livdwhtst01:[oracle]:pwd
/ora/admin/MIDDWHT3/udump

livdwhtst01:[oracle]:ls -ltra | tail
-rw-r----- 1 oracle dba 644 Jul 21 07:25 middwht3_ora_8618.trc
-rw-rw-rw- 1 oracle dba 1561 Jul 21 08:26 middwht3_ora_16958.trc
-rw-r----- 1 oracle dba 32723 Jul 21 08:52 middwht3_ora_26349.trc
-rw-r----- 1 oracle dba 646 Jul 21 11:37 middwht3_ora_17201.trc
-rw-r----- 1 oracle dba 109697 Jul 21 11:59 middwht3_ora_21441.trc
-rw-r----- 1 oracle dba 59396 Jul 21 12:06 middwht3_ora_22680.trc
-rw-r----- 1 oracle dba 42243 Jul 21 12:16 middwht3_ora_23474.trc
-rw-r----- 1 oracle dba 646 Jul 21 12:18 middwht3_ora_24621.trc
drwxrwxr-x 2 oracle dba 224256 Jul 21 13:14 .
-rw-r----- 1 oracle dba 21537 Jul 21 13:15 middwht3_ora_9429.trc
livdwhtst01:[oracle]:cat middwht3_ora_9429.trc
/ora/admin/MIDDWHT3/udump/middwht3_ora_9429.trc Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - 64bit Production With the Partitioning and Data Mining options ORACLE_HOME = /ora/product/10.2.0 System name: SunOS Node name: livdwhtst01 Release: 5.9 Version: Generic_118558-17 Machine: sun4us Instance name: MIDDWHT3 Redo thread mounted by this instance: 1 Oracle process number: 13 Unix process pid: 9429, image: oracle@livdwhtst01 (TNS V1-V3) *** 2006-07-21 13:14:59.446 *** ACTION NAME:() 2006-07-21 13:14:59.445 *** MODULE NAME:(SQL*Plus) 2006-07-21 13:14:59.445 *** SERVICE NAME:(SYS$USERS) 2006-07-21 13:14:59.445 *** SESSION ID:(89.10029) 2006-07-21 13:14:59.445 sql_id=7b2twsn8vgfsc. Current SQL statement for this session: select count(*) from test ============ Plan Table ============ --------------------------------------+-----------------------------------+ | Id | Operation | Name | Rows | Bytes | Cost | Time | --------------------------------------+-----------------------------------+ | 0 | SELECT STATEMENT | | | | 328 | | | 1 | SORT AGGREGATE | | 1 | | | | | 2 | TABLE ACCESS FULL | TEST | 104K | | 328 | 00:00:04 | --------------------------------------+-----------------------------------+ Predicate Information: ---------------------- Content of other_xml column =========================== db_version : 10.2.0.1 parse_schema : TESTUSER plan_hash : 3467505462 Outline Data: /*+ BEGIN_OUTLINE_DATA IGNORE_OPTIM_EMBEDDED_HINTS OPTIMIZER_FEATURES_ENABLE('10.2.0.1') ALL_ROWS OUTLINE_LEAF(@"SEL$1") FULL(@"SEL$1" "TEST"@"SEL$1") END_OUTLINE_DATA */ Optimizer environment: optimizer_mode_hinted = false optimizer_features_hinted = 0.0.0 parallel_execution_enabled = true parallel_query_forced_dop = 0 parallel_dml_forced_dop = 0 parallel_ddl_forced_degree = 0 [Lots of other parameter values snipped out here ] _force_rewrite_enable = false _optimizer_star_tran_in_with_clause = true _optimizer_complex_pred_selectivity = true _gby_hash_aggregation_enabled = true Query Block Registry: ********************* SEL$1 0x7b01b6a8 (PARSER) [FINAL] Optimizer State Dump: call(in-use=19392, alloc=49080), compile(in-use=59056, alloc=107040)


You'll also see the SQL statement that results from the show parameter user_dump_test instruction (I've stripped most of the output to keep it shorter)

sql_id=gz3s8nmsh6s8c.
Current SQL statement for this session:
SELECT NAME NAME_COL_PLUS_SHOW_PARAM,DECODE(TYPE,:"SYS_B_00",:"SYS_B_01",:"SYS_B_02",:"SYS_B_03",

:"SYS_B_04",:"SYS_B_05",:"SYS_B_06",:"SYS_B_07",:"SYS_B_08",:"SYS_B_09",        

:"SYS_B_10",:"SYS_B_11", :"SYS_B_12") TYPE,DISPLAY_VALUE VALUE_COL_PLUS_SHOW_PARAM 

FROM V$PARAMETER WHERE UPPER(NAME) LIKE UPPER(:"SYS_B_13") ORDER BY NAME_COL_PLUS_SHOW_PARAM,ROWNUM

============
Plan Table
============
---------------------------------------+-----------------------------------+
| Id  | Operation            | Name    | Rows  | Bytes | Cost  | Time      |
---------------------------------------+-----------------------------------+
| 0   | SELECT STATEMENT     |         |       |       |     2 |           |
| 1   |  SORT ORDER BY       |         |     1 |   371 |     2 |  00:00:01 |
| 2   |   COUNT              |         |       |       |       |           |
| 3   |    HASH JOIN         |         |     1 |   371 |     1 |  00:00:01 |
| 4   |     FIXED TABLE FULL | X$KSPPI |     1 |    81 |     0 |           |
| 5   |     FIXED TABLE FULL | X$KSPPCV|   100 |   28K |     0 |           |
---------------------------------------+-----------------------------------+
Predicate Information:
----------------------
3 - access("X"."INDX"="Y"."INDX")
3 - filter((TRANSLATE("KSPPINM",'_','#') NOT LIKE '#%' OR "KSPPSTDF"='FALSE' OR BITAND("KSPPSTVF",5)>0))
4 - filter(("X"."INST_ID"=USERENV('INSTANCE') AND UPPER("KSPPINM") LIKE UPPER(:SYS_B_13) AND 
           TRANSLATE("KSPPINM",'_','#') NOT LIKE '##%'))

Content of other_xml column
===========================
  db_version     : 10.2.0.1
  parse_schema   : TESTUSER
  plan_hash      : 245223518
  Outline Data:
  /*+
    BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('10.2.0.1')
      ALL_ROWS
      OUTLINE_LEAF(@"SEL$88122447")
      MERGE(@"SEL$641071AC")
      OUTLINE(@"SEL$1")
      OUTLINE(@"SEL$641071AC")
      MERGE(@"SEL$07BDC5B4")
      OUTLINE(@"SEL$2")
      OUTLINE(@"SEL$07BDC5B4")
      MERGE(@"SEL$4")
      OUTLINE(@"SEL$3")
      OUTLINE(@"SEL$4")
      FULL(@"SEL$88122447" "X"@"SEL$4")
      FULL(@"SEL$88122447" "Y"@"SEL$4")
      LEADING(@"SEL$88122447" "X"@"SEL$4" "Y"@"SEL$4")
      USE_HASH(@"SEL$88122447" "Y"@"SEL$4")
    END_OUTLINE_DATA
  */
Posted by Doug Burns Comments: (8) Trackbacks: (0)

Trackbacks
Trackback specific URI for this entry

No Trackbacks

Comments
Display comments as (Linear | Threaded)

#1 - Pete_S said:
2006-07-21 19:16 - (Reply)

I've taken a look at V_$SEGSTAT at the begining and end of the user day and measured changes in IO - not rocket science and probably over simplistic but to my (non-DBA) brain no IO - physical or logical then no access.

#1.1 - Doug Burns said:
2006-07-21 21:02 - (Reply)

Pete,

That's one of the better possibilities and I'm not sure it is over-simplistic, although it does lack that 'what caused it' factor which would help us see whether it was a 'useful' activity.

As I said, there are a few approaches, I just haven't seen this one mentioned often.

Cheers

#1.2 - Naresh 2006-07-22 18:47 - (Reply)

Wouldn't the reads performed for index maintenance show up in v$segstat ?

#1.2.1 - Pete_S said:
2006-07-23 12:20 - (Reply)

I was thinking that in a DW the tables are only updated in the batch - there is no index update during the time users query the tables. Of course this is not a good technique for 'OLTP' work as updates can happen at anytime

#2 - Howard J. Rogers said:
2006-07-23 23:39 - (Reply)

Late to the party as ever, but I discussed this sort of thing ages ago:

http://dizwell.com/main/content/view/93/198/

Regards as ever.

#3 - Doug Burns said:
2006-07-24 23:12 - (Reply)

That's a really nice article Howard.

To me, the best approach is maybe a little application and certainly version dependant. As this is 10.2, we'd like to use AWR but are still sorting out the licencing implications.

Cheers,

Doug

#4 - David Aldridge said:
2006-07-25 19:42 - (Reply)

I find the segment statistics to be very useful myself. It's an interesting exercise to work out a buffer cache hit ratio at the segment level. In particular it's also a very fast way of determining how effective partition pruning has been.

#5 - Dave Edwards said:
2006-07-28 19:21 - (Reply)

A useful post, Doug. Bill T. has it covered in this week's Log Buffer too -- http://gruntdba.wordpress.com/2006/07/28/log-buffer-3/ .

Cheers, Dave.


Add Comment

Standard emoticons like :-) and ;-) are converted to images.
E-Mail addresses will not be displayed and will only be used for E-Mail notifications
BBCode format allowed
 
 

Upcoming Appearances

Hotsos Symposium 2010 - 7th-11th March

Comments

Doug Burns about Advert: Symposium Countdown
Tue, 09.02.2010 11:27
I can still see a new server in my near future too!
Doug Burns about Advert: Symposium Countdown
Tue, 09.02.2010 09:03
Well I'll be running stuff in VMs, that's for sure, and people have gone on and [...]
Pete Scott about Advert: Symposium Countdown
Tue, 09.02.2010 08:55
It is such a relief to get the paper in (so well done, Doug).... I dispatched [...]
Links in Comments

It's a minor source of frustration to me that you can't just paste a Hypertext link into the comments form here but, should you ever want to include a link, all you need to do is use the BBCode format, as mentioned below the comment form.

Here is a link to the relevant part of the document that explains how.

Bookmark

Open All | Close All

Syndicate This Blog

  • XML RSS 2.0 feed
  • ATOM/XML ATOM 1.0 feed
  • XML RSS 2.0 Comments
  • Feedburner Feed

Powered by

Serendipity PHP Weblog

Show tagged entries

xml 11g
xml ACE
xml adaptive thresholds
xml ash
xml Audit Vault
xml AWR
xml Blogging
xml Cuddly Toys
xml Database Refresh
xml Direct Path Reads
xml Fun
xml listener
xml locking
xml oow
xml oow2009
xml OTN
xml Parallel
xml Patching
xml Swingbench
xml The Reality Gap
xml Time Matters
xml Unix/Shell
xml Useful Links

Disclaimer

For the avoidance of any doubt, all views expressed here are my own and not those of past or current employers, clients, friends, Oracle Corporation, my Mum or, indeed, Flatcat. If you want to sue someone, I suggest you pick on Tigger, but I hope you have a good lawyer. Frankly, I doubt any of the former agree with my views or would want to be associated with them in any way.

Design by Andreas Viklund | Conversion to s9y by Carl