Mohamed Houri’s Oracle Notes

July 27, 2013

Index Coalesce : sys_op_lbid

Filed under: Oracle — hourim @ 8:44 am

A recent question on the oracle French forum about rebuilding indexes remembered me to write a small note to show the existence of a more interesting option to use when maintaining an index but unfortunately not widely used. It is the COALESCE index command. Let me put a simple model and explain (a) how to identify indexes that might benefit from the COALESCE and (b) how to show the effect of the COALESCE command on this kind of indexes.

create table t1 as
 select
 trunc(sysdate) + rownum d1,
 rownum n1
 from dual
 connect by level <= 1e6
 ;
create index ind_d1 on t1(d1);

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

In order to simulate a real life example of an index evolution I will try to update this index several times for the same range of data until it reaches a final stage

begin
 2 for j in 1..100
 3 loop
 4 update t1
 5 set d1 = trunc(d1) + j
 6 where t1.n1 between 15000 and 20000;
 7 end loop;
 8 commit;
 9 end;
 10 /

PL/SQL procedure successfully completed.

begin
 2 for j in 1..100
 3 loop
 4 update t1
 5 set d1 = trunc(d1) - j
 6 where t1.n1 between 15000 and 20000;
 7 end loop;
 8 commit;
 9 end;
 10 /

PL/SQL procedure successfully completed.

begin
 2 for j in 1..100
 3 loop
 4 update t1
 5 set d1 = trunc(d1) - j+1
 6 where t1.n1 between 15000 and 20000;
 7 end loop;
 8 commit;
 9 end;
 10 /

In order to know if my index can benefit from the coalesce command, I will use the sys_op_lbid internal oracle function which gives the number of Keys(leaf block id) per blocks.

select object_name,object_id
from user_objects
where object_name = 'IND_D1';

OBJECT_NAME OBJECT_ID
-------------------- ----------
IND_D1 509423

select
 2 keys_per_leaf, count(*) blocks
 3 from (
 4 select sys_op_lbid (509423, 'L', t1.rowid) block_id,
 5 count (*) keys_per_leaf
 6 from t1
 7 where d1 is not null
 8 group by sys_op_lbid (509423, 'L', t1.rowid)
 9 )
 10 group by keys_per_leaf
 11 order by keys_per_leaf;

KEYS_PER_LEAF BLOCKS
------------- ----------
 2 102
 3 451
 4 352
 5 133
 6 153
 7 72
 8 98
 9 22
 10 63
…..
 134 1
 140 1
 156 1
 196 1
 206 1
 215 1
 292 1
 377 2611
62 rows selected.

This is a smashed index as you might have pointed out that in order to access to 2 leaf block keys we need to visit 102 blocks worth of data and to get 3 other leaf block keys we need to access 451 blocks and so on. This index needs really to be COALESCED. Before coalescing it let me show you how space is spread within this index:

 begin
 p_check_free_space (user, 'IND_D1', 'INDEX');
 end;
 /
Number of Blocks with 0-25% free space = 0 -------> Total Bytes = 0
Number of Blocks with 25-50% free space = 1459 -------> Total Bytes = 11.3984375
Number of Blocks with 50-75% free space = 0 -------> Total Bytes = 0
Number of Blocks with 75-100% free space = 0 -------> Total Bytes = 0
Number of Full Blocks with no free space = 4407 -------> Total Bytes = 34.4296875

Total Blocks : 5866
Total Size MB: 46.928

Now it’s time to coalesce this index and get its new leaf block key distribution

alter index ind_d1 coalesce;

Index altered.

select
 2 keys_per_leaf, count(*) blocks
 3 from (
 4 select sys_op_lbid (509423, 'L', t1.rowid) block_id,
 5 count (*) keys_per_leaf
 6 from t1
 7 where d1 is not null
 8 group by sys_op_lbid (509423, 'L', t1.rowid)
 9 )
 10 group by keys_per_leaf
 11 order by keys_per_leaf;

KEYS_PER_LEAF BLOCKS
------------- ----------
 14 1
 29 1
 35 1
 167 1
 196 1
 210 1
 309 1
 367 1
 377 2649

9 rows selected.

Spot how the index become now (after it has been coalesced) more attractive as far as to get 14 (29, 35 or 367) keys of leaf blocks id we need to visit only one block.

Finally let me show you the new index space configuration the coalesce command has produced

begin
 p_check_free_space (user, 'IND_D1', 'INDEX');
end;
/
Number of Blocks with 0-25% free space = 0 -------> Total Bytes = 0
Number of Blocks with 25-50% free space = 3197 -------> Total Bytes = 24.9765625
Number of Blocks with 50-75% free space = 0 -------> Total Bytes = 0
Number of Blocks with 75-100% free space = 0 -------> Total Bytes = 0
Number of Full Blocks with no free space = 2669 -------> Total Bytes = 20.85

Total Blocks :5866
Total Size MB:46.928

The coalesce command did not changed the size of the index nor the number of its total blocks. However, it did make a nice data distribution since it freed up 1736 new blocks ( 4407-26669) and made 1768 (3197-1429) new blocks offering 25-50% of free space.

Before closing this blog article I would like to emphasize that if you arrive to the conclusion that your index needs to be coalesced then think about a periodic (each week for example) coalesce of this index. Because the conditions which smashed your index (delete of old data from left hand side of the index and insert new data into the right hand side of the index) is still present and will sooner or later smash your index so that a new coalesce will be necessary.

Blog at WordPress.com.

Tony's Oracle Tips

Tony Hasler's light hearted approach to learning about Oracle

Richard Foote's Oracle Blog

Focusing Specifically On Oracle Indexes, Database Administration and Some Great Music

Hatem Mahmoud's blog

Just another blog : Databases, Linux and other stuffs

Mohamed Houri’s Oracle Notes

Qui se conçoit bien s’énonce clairement

Oracle Diagnostician

Performance troubleshooting as exact science

Raheel's Blog

Things I have learnt as Oracle DBA

Coskan's Approach to Oracle

What I learned about Oracle

So Many Oracle Manuals, So Little Time

“Books to the ceiling, Books to the sky, My pile of books is a mile high. How I love them! How I need them! I'll have a long beard by the time I read them”—Lobel, Arnold. Whiskers and Rhymes. William Morrow & Co, 1988.

Carlos Sierra's Tools and Tips

Tools and Tips for Oracle Performance and SQL Tuning

Oracle Scratchpad

Just another Oracle weblog

OraStory

Dominic Brooks on Oracle Performance, Tuning, Data Quality & Sensible Design ... (Now with added Sets Appeal)