Skip to Main Content
  • Questions
  • LIOs, how to ensure which are the ones I am looking for

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Joaquin.

Asked: December 24, 2004 - 4:07 pm UTC

Last updated: April 01, 2005 - 6:13 pm UTC

Version: 8.1.7

Viewed 1000+ times

You Asked

Hi Tom, merry christmas from the caribbean. My question is about how to get an estimation of how many LIOs would be needed to retrieve a given row from a table. I haven't been able to find any formula for this. It is something based on the avg_row_len of the table I am querying or it is happen to be indexes statistics or is there happen to exists some formula I can rely on ? .. the reason I am curious about this is that at the company I work at, I've seen that querying different tables gives different amount of LIOs. I have done test with almost identical number of rows ( millions ) and some with a few rows. Allthough they have almost equally number of rows, lios are very different.

If you can point me in a direction on where to read about this or something would be very very appreciated.

Thank you!

and Tom said...

Say you are going to access a single row in the table via an index (where <indexed_set_of_columns> = value) - for example:


create table t ( x int, y int, z int );
create index t_idx on t(x,y);

and you issue:

select * from t where x=:x and y=:y;

and you expect 1 row. the number of LIOs will be:

height of index + 1 or 2 -- in general. that is, say the index has a height of three (root -> branch -> leaf node). That'll be 3 LIOs to find the key entry in the leaf block. That key entry will have a rowid -- we can use that rowid to then read the file/block the data is on. That'll be one LIO -- unless the row is migrated (or chained) then then will be an LIO to get the migrated row (or as many LIO's as it takes to get the entire chained row reassembled)..


But -- if your index was just on t(x) for example, you might see tons more LIO's to get the row -- because we have to skip back and forth between the index and the table to get all of the x/y values and check them out.

Lets take a look at an example:

ops$tkyte@ORA9IR2> /*
DOC>
DOC>drop table t;
DOC>
DOC>create table t
DOC>( x int,
DOC> y int,
DOC> z int,
DOC> a char(2000) default 'x',
DOC> b char(2000) default 'x',
DOC> c char(2000) default 'x',
DOC> d char(2000) default 'x',
DOC> e char(2000) default 'x',
DOC> f char(2000) default 'x',
DOC> g char(2000) default 'x',
DOC> h char(2000) default 'x',
DOC> i char(2000) default 'x',
DOC> j char(2000) default 'x',
DOC> k char(2000) default 'x',
DOC> l char(2000) default 'x',
DOC> m char(2000) default 'x',
DOC> n char(2000) default 'x',
DOC> o char(2000) default 'x',
DOC> p char(2000) default 'x'
DOC>)
DOC>/
DOC>
DOC>insert into t ( x,y,z )
DOC>select mod(rownum,100), rownum, rownum
DOC> from all_objects
DOC> where rownum <= 5000
DOC>/
DOC>create index t_idx1 on t(x,y);
DOC>create index t_idx2 on t(x);
DOC>*/
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> variable x number
ops$tkyte@ORA9IR2> variable y number
ops$tkyte@ORA9IR2> exec :x := 1; :y := 1;

PL/SQL procedure successfully completed.

ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> @trace
ops$tkyte@ORA9IR2> alter session set events '10046 trace name context forever, level 12';

Session altered.

ops$tkyte@ORA9IR2> select /*+ index( t t_idx1 ) */ x,y,z from t where x = :x and y = :y;

X Y Z
---------- ---------- ----------
1 1 1

ops$tkyte@ORA9IR2> select /*+ index( t t_idx2 ) */ x,y,z from t where x = :x and y = :y;

X Y Z
---------- ---------- ----------
1 1 1

ops$tkyte@ORA9IR2> select /*+ index( t t_idx1 ) */ x,y,z,substr(p,1,1) from t where x = :x and y = :y;

X Y Z S
---------- ---------- ---------- -
1 1 1 x

ops$tkyte@ORA9IR2> select /*+ index( t t_idx2 ) */ x,y,z,substr(p,1,1) from t where x = :x and y = :y;

X Y Z S
---------- ---------- ---------- -
1 1 1 x


looking at the tkprof we can see what happened:


select /*+ index( t t_idx1 ) */ x,y,z from t where x = :x and y = :y

Rows Row Source Operation
------- ---------------------------------------------------
1 TABLE ACCESS BY INDEX ROWID T (cr=5 r=0 w=0 time=68 us)
1 INDEX RANGE SCAN T_IDX1 (cr=3 r=0 w=0 time=41 us)(object id 35623)

best case -- good use of index, 3 consistent reads (cr=) to get to the row in the index -- then just the lio's to retrieve the row from the table


********************************************************************************
select /*+ index( t t_idx2 ) */ x,y,z from t where x = :x and y = :y

Rows Row Source Operation
------- ---------------------------------------------------
1 TABLE ACCESS BY INDEX ROWID T (cr=89 r=0 w=0 time=389 us)
50 INDEX RANGE SCAN T_IDX2 (cr=3 r=0 w=0 time=87 us)(object id 35624)

here, a poor choice of indexing had us hit the table 50 times, to peek at Y to discover we didn't want that record. The extra CR's here are all about the hits on the table

********************************************************************************
select /*+ index( t t_idx1 ) */ x,y,z,substr(p,1,1) from t where x = :x and y = :y

Rows Row Source Operation
------- ---------------------------------------------------
1 TABLE ACCESS BY INDEX ROWID T (cr=9 r=0 w=0 time=82 us)
1 INDEX RANGE SCAN T_IDX1 (cr=3 r=0 w=0 time=42 us)(object id 35623)

here we see the effect of piecing the enire row back together -- I was massively chained (this is an unusual case, I made the row really really wide, you'll see this with LONGS and LONGS raws alot though)

********************************************************************************
select /*+ index( t t_idx2 ) */ x,y,z,substr(p,1,1) from t where x = :x and y = :y

Rows Row Source Operation
------- ---------------------------------------------------
1 TABLE ACCESS BY INDEX ROWID OBJ#(35622) (cr=93 r=0 w=0 time=444 us)
50 INDEX RANGE SCAN OBJ#(35624) (cr=3 r=0 w=0 time=92 us)(object id 35624)

and the same sort of effect when we use the bad index


So, if you are doing simple table accesses and seeing high LIO's -- you probably have the wrong indexes in place (The indexes are not "fat enough", we have the visit the table lots to evaluate the predicate)

Rating

  (3 ratings)

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

Comments

Why range scan?

Peter Tran, December 25, 2004 - 1:56 pm UTC

Hi Tom,

Merry Christmas!

Why in your first example is the Optimizer doing an "INDEX RANGE SCAN" when using t_idx1? This index contains all the column in your predicate.

Thanks,
-Peter

Tom Kyte
December 26, 2004 - 12:20 pm UTC

the index is not unique, there could be 5,000,000,000 records in the index such that x=1 and y=1.


it has to range scan it -- find the first and last records that meet the criteria.

A reader, December 25, 2004 - 3:28 pm UTC


Pls clarify

A reader, April 01, 2005 - 5:28 pm UTC

Tom,

In your example, the CR for index is 3. Ideally it should be a little more than 3 because of the SQL Array size etc. ie. once the default array size of 15 is exceeded, it has to again get the blocks and so ideally consistent reads for index should be little more than 3. Right?

Tom Kyte
April 01, 2005 - 6:13 pm UTC

they all got one row back to the client, if the client had to fetch and fetch -- yes the LIO's would have gone up, in this case, no

More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library