Skip to Main Content
  • Questions
  • Index organized table, secondary indexes, and stale guesses

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, AndrE.

Asked: November 01, 2017 - 5:58 am UTC

Last updated: November 01, 2017 - 12:05 pm UTC

Version: 11.2

Viewed 1000+ times

You Asked

Hi, Tom

Secondary indexes of IOT have physical guesses. However guesses can become stale after many inserts.

It is two ways to obtain fresh guesses:

1. Alter index ... rebiuld and
2. ALTER INDEX ... UPDATE BLOCK REFERENCES.

3. What is better or quicker? Why?

If all guesses of index is fresh, why query plan is INDEX UNIQUE SCAN INDEX RANGE SCAN IX_ONAME, but not TABLE ACCESS BY INDEX ROWID?

4. Where can I see Oracle have read the quess in IOT, not primary key?


Thanks

and Chris said...

With update block references, the database is just scanning through the index to fix the stale guesses. Whereas a rebuild is essentially recreating it.

One side effect of this is a rebuild may make the index smaller. But updating references has no impact on the size:

create table t (
  x int, y int, z int,
  constraint pk primary key (x, y)
) organization index;

create index i on t (z);

exec dbms_random.seed(0);
insert into t 
  select x.r, y.r, dbms_random.value(1, 200)
  from  (select level r from dual connect by level <= 10000) x,
        (select level r from dual connect by level <= 5) y;
        
commit;

exec dbms_stats.gather_table_stats(user, 't');

select blevel, leaf_blocks
from   user_indexes
where  index_name = 'I';

BLEVEL   LEAF_BLOCKS   
       1           236 

alter index i update block references;

exec dbms_stats.gather_table_stats(user, 't');

select blevel, leaf_blocks
from   user_indexes
where  index_name = 'I';

BLEVEL   LEAF_BLOCKS   
       1           236 

truncate table t;
exec dbms_random.seed(0);
insert into t 
  select x.r, y.r, dbms_random.value(1, 200)
  from  (select level r from dual connect by level <= 10000) x,
        (select level r from dual connect by level <= 5) y;
        
commit;

exec dbms_stats.gather_table_stats(user, 't');

select blevel, leaf_blocks
from   user_indexes
where  index_name = 'I';

BLEVEL   LEAF_BLOCKS   
       1           236 

alter index i rebuild;

exec dbms_stats.gather_table_stats(user, 't');

select blevel, leaf_blocks
from   user_indexes
where  index_name = 'I';

BLEVEL   LEAF_BLOCKS   
       1           157 


Given that a rebuild recreates the index, unsurprisingly it does more work as the following tests show:

truncate table t;
exec dbms_random.seed(0);
insert into t 
  select x.r, y.r, dbms_random.value(1, 200)
  from  (select level r from dual connect by level <= 10000) x,
        (select level r from dual connect by level <= 5) y;
        
commit;

exec runstats_pkg.rs_start;
alter index i update block references;
exec runstats_pkg.rs_pause;

exec dbms_random.seed(0);
truncate table t;
insert into t 
  select x.r, y.r, dbms_random.value(1, 200)
  from  (select level r from dual connect by level <= 10000) x,
        (select level r from dual connect by level <= 5) y;
        
commit;

exec runstats_pkg.rs_resume;
alter index i rebuild;
exec runstats_pkg.rs_stop(1000, false);

===============================================================================================
RunStats report : 01-NOV-2017 04:53:24
===============================================================================================


-----------------------------------------------------------------------------------------------
1. Summary timings
-----------------------------------------------------------------------------------------------

Type  Name                                                       Run1         Run2         Diff
----- -------------------------------------------------- ------------ ------------ ------------
TIMER cpu time (hsecs)                                             20           26            6
TIMER elapsed time (hsecs)                                         68           91           23

Comments:
1) Run1 was 25.3% quicker than Run2
2) Run1 used 25.3% less CPU time than Run2


-----------------------------------------------------------------------------------------------
2. Statistics report
-----------------------------------------------------------------------------------------------

Type  Name                                                       Run1         Run2         Diff
----- -------------------------------------------------- ------------ ------------ ------------
LATCH KTU in-memory txn table latch                             1,186          116       -1,070
STAT  txn cache local writes                                    1,185          115       -1,070
LATCH object queue header operation                                86        1,243        1,157
STAT  db block changes                                          2,154          550       -1,604
LATCH simulator hash latch                                         22        2,805        2,783
STAT  redo size for direct writes                                   0        3,060        3,060
STAT  KTFB alloc time (ms)                                          0        3,273        3,273
LATCH cache buffers chains                                    106,462      116,875       10,413
STAT  file io wait time                                           235       41,699       41,464
STAT  no buffer to keep pinned count                                0       50,000       50,000
STAT  sorts (rows)                                                  0       50,000       50,000
STAT  session uga memory                                            0       51,824       51,824
STAT  undo change vector size                                  79,628        8,116      -71,512
STAT  redo size                                               260,532       62,632     -197,900
STAT  physical read bytes                                     245,760      819,200      573,440
STAT  physical read total bytes                               245,760      819,200      573,440
STAT  logical read bytes from cache                       417,611,776  416,915,456     -696,320
STAT  physical write bytes                                          0    1,286,144    1,286,144
STAT  physical write total bytes                                    0    1,286,144    1,286,144
STAT  cell physical IO interconnect bytes                     245,760    2,105,344    1,859,584
STAT  KTFB alloc space (block)                                      0    2,097,152    2,097,152
STAT  session pga memory                                   -3,932,160      131,072    4,063,232


-----------------------------------------------------------------------------------------------
3. Latching report
-----------------------------------------------------------------------------------------------

Type  Name                                                       Run1         Run2         Diff
----- -------------------------------------------------- ------------ ------------ ------------
LATCH total latches used                                      112,421      125,178       12,757

Comments:
1) Run1 used 10.2% fewer latches than Run2


-----------------------------------------------------------------------------------------------
4. About
-----------------------------------------------------------------------------------------------
- RunStats v2.01 by Adrian Billington (http://www.oracle-developer.net)
- Based on the original RUNSTATS utility by Tom Kyte

===============================================================================================
End of report
===============================================================================================


Tests run on 12.2.0.1.

Run 1 is uses update block references and is faster, uses fewer latches and a lot less work on many stats (notably sorts and physicaly writes which are both zero).

So if all you want to do is fix stale guesses, update block references looks like the way to go.

If all guesses of index is fresh, why query plan is INDEX UNIQUE SCAN INDEX RANGE SCAN IX_ONAME, but not TABLE ACCESS BY INDEX ROWID?

Becuase there isn't a table to access! With an IOT the table is an index. So "TABLE ACCESS BY INDEX ROWID" is an invalid access method.

Where can I see Oracle have read the quess in IOT, not primary key?

Not sure what you mean by this. Could you clarify?

If you want to know more about this, I recommend reading Richard Foote's articles on this:

https://richardfoote.wordpress.com/2012/04/26/iot-secondary-indexes-the-logical-rowid-guess-component-part-i-lucky/
https://richardfoote.wordpress.com/2012/05/08/iot-secondary-indexes-the-logical-rowid-guess-component-part-ii-move-on/

Rating

  (1 rating)

Is this answer out of date? If it is, please let us know via a Comment

Comments

Secondary indexes of IOT

AndrE, November 02, 2017 - 4:49 am UTC

Thanks, Chris

It is clear now.
A special thank you for the links.
It is very informative article.

More to Explore

Design

New to good database design? Check out Chris Saxon's full fundamentals class.