Skip to Main Content

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Timofey.

Asked: November 22, 2005 - 10:53 am UTC

Last updated: April 26, 2007 - 11:55 am UTC

Version: 9.2.0

Viewed 1000+ times

You Asked

Hi Tom.

I notice very strange thing about indexes on table with no rows.
Here is listing:



SQL> SET AUTOTRACE OFF
SQL>
SQL> DROP TABLE t1;

Table dropped.

SQL> CREATE TABLE t1 NOLOGGING AS SELECT CAST(MOD(ROWNUM, 2) AS NUMBER) col1 FROM all_tab_columns;

Table created.

SQL>
SQL> CREATE INDEX ind1 ON t1(col1) NOLOGGING;

Index created.

SQL>
SQL> SET AUTOTRACE ON EXPLAIN STATISTICS
SQL>
SQL> SELECT COUNT(*) FROM t1 WHERE col1 = 0;

COUNT(*)
----------
53706


Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 SORT (AGGREGATE)
2 1 INDEX (RANGE SCAN) OF 'IND1' (NON-UNIQUE)




Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
99 consistent gets
98 physical reads
0 redo size
381 bytes sent via SQL*Net to client
499 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed


>>> 99 consistent gets. It is OK. But after delete......


SQL>
SQL> DELETE FROM t1;

107412 rows deleted.


Execution Plan
----------------------------------------------------------
0 DELETE STATEMENT Optimizer=CHOOSE
1 0 DELETE OF 'T1'
2 1 TABLE ACCESS (FULL) OF 'T1'




Statistics
----------------------------------------------------------
162 recursive calls
328838 db block gets
250 consistent gets
104 physical reads
44464008 redo size
620 bytes sent via SQL*Net to client
510 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
2 sorts (memory)
0 sorts (disk)
107412 rows processed

SQL> COMMIT;

Commit complete.

SQL>
SQL> SELECT COUNT(*) FROM t1 WHERE col1 = 0;

COUNT(*)
----------
0


Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 SORT (AGGREGATE)
2 1 INDEX (RANGE SCAN) OF 'IND1' (NON-UNIQUE)




Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
99 consistent gets
0 physical reads
0 redo size
378 bytes sent via SQL*Net to client
499 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed


>>>>> Wow! To read 0 rows index we need again 99 consistent gets!


SQL>
SQL> ALTER INDEX ind1 REBUILD;

Index altered.

SQL>
SQL> SELECT COUNT(*) FROM t1 WHERE col1 = 0;

COUNT(*)
----------
0


Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 SORT (AGGREGATE)
2 1 INDEX (RANGE SCAN) OF 'IND1' (NON-UNIQUE)




Statistics
----------------------------------------------------------
14 recursive calls
0 db block gets
4 consistent gets
0 physical reads
0 redo size
378 bytes sent via SQL*Net to client
499 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed

SQL>
SQL>

I understand why only 4 consistent gets in last select (after rebuild) but why there are 99 after delete?
Why empty index read requires same amount of consistent gets as the full one?

We need to clean many tables with foreign keys to each other. Can not use truncate - we clean 90-99% of table data. Do we need to rebuild indexes after delete to speedup further FK check on next table delete?




and Tom said...

because the structure is still there, it is the same if you had no index - the table would still full scan, and the table would be there.

Over time, blocks are moved in and out of index structures - but in this single transaction - it would not happen.


And don't forget - you are reading every row that was. For a foreign key check, it won't read every row that way, simple index range scan that STOPS.


It could likely be a case where you would consider rebuilding or coalescing the index after removing 99% of the data (sort of an 'atypical' thing to do)

Rating

  (7 ratings)

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

Comments

block reusage

Timofey, November 24, 2005 - 7:59 am UTC

So if I delete all (or 99%) rows from table, Oracle do not remove links to empty leaf blocks from upper level (branch) blocks (or mark it as "empty")? And index range scan visit first empty block, find no rows, go to the next block, find no rows etc...?

When and how Oracle reuse those blocks?

Tom Kyte
November 24, 2005 - 9:02 am UTC

when you start loading up the table again, it'll unlink some blocks and just in general reuse the space.

clarification, and queue-indexes

Alberto Dell'Era, November 25, 2005 - 3:21 pm UTC

Hi Tom,

a couple of clarifications/dissertations if you don't mind:

(a) My understanding is - when an index block is marked as free, the next operation on the index that needs a new block (eg insert before the "tail" or after the "head", block split), will unlink and reuse the block, instead of increasing the index segment HWM. Correct ?

(b) Just yesterday I found a bunch of statements that showed high up in a Statspack report - thousands of consistent gets on *empty* indexes, lot of cpu burning.

They are all indexes on queue tables (home-grown or created by AQ), basically on the column(s) that define the queue order - enqued rows go after the head of the queue, the dequeue operation read from the tail, and then either delete the row or mark the indexed column(s) as null.

Is it correct to say that, in this case, it takes only a burst on the queue dimension (eg dequers not keeping up with the enquers) to make the index "big", so increasing the subsequent dequeue operations resource usage "forever" ?

I mean - when the queue is back to its normal size (say zero), new rows will go in a block after the head, reusing a block - but the index size will be "high" forever (until Alberto comes in and coalesce the index).

(c) Assuming that Alberto doesn't like to watch after the index daily - what may be a good algorithm to check when the index needs coalescing ?

Thanks in advance!

Tom Kyte
November 25, 2005 - 3:53 pm UTC

(a) it can, yes. (it is fairly complex, Steve Adams explained it in a seminar I went to - it is fairly involved)

(b) yes, it can.

These sweeper indexes are the ones that are the most likely candidates for coalesce/rebuild. They are in a class by themselves.


(c) monitor the sql that uses the index and when the consistent gets exceed some threshold (v$sql_plan_statistics if populated could be very handy).....

Alberto Dell'Era, November 25, 2005 - 5:16 pm UTC

> (c) monitor the sql that uses the index and when the consistent gets exceed
> some threshold (v$sql_plan_statistics if populated could be very handy).....

I don't think that I can afford to set statistics_level=all on that instance (I'll investigate, but I'm afraid I can't). And in the case of AQ, when we upgrade or patch, we will need to recode everything for the new (undocumented) AQ SQL statements ...

But isn't there any structural check that we can perform to identify sweepers - such as detecting indexes with a long, hollow tail ?

I know how to calculate the index free space, but of course lot of free space alone tells nothing - it may be a non-sweeper that simply needs to have a big amount of empty space to avoid block splits.

I suspect that in that database there are a lot of sweepers, since I know that there are a lot of "queue tables" - that would be another reason to detect them "somehow" before they surface in the staspack.

Ofir Manor, November 25, 2005 - 7:16 pm UTC

Hi Alberto, Tom,
I think what you're looking for is the Segment Advisor.
It is the built-in mechanism in 10g that identifies segments with wasted space for you and can help you easily reclaim the space. It can recommend candidate tables for online segment shrink / online table reorganization(depending on whether this is ASSM tablespace or not) and also index coalesce. In 10g Release 2, it should run automaticly in the maintainance window...
</code> http://download-uk.oracle.com/docs/cd/B19306_01/server.102/b14231/schema.htm#i1006922 <code>
Anyway, if you have identified a set of tables/indexes that would benefit from a nightly shrinking (all your queue tables), can't you just add a job to shrink them every night ? it should probably be very fast, because you said that their regular state is being empty/almost empty.


Tom Kyte
November 26, 2005 - 11:20 am UTC

while that might be true - many people do not have access to it, it is part of the tuning pack available with enterprise edition.

Alberto Dell'Era, November 26, 2005 - 5:35 am UTC

Hi Ofir,

but will the Segment Advisor distinguish between an index that has a lot of free blocks

(a) in the tail (head) only (=sweepers)
(b) scattered randomly here and there ?

(a) is probably an excellent candidate for coalescing, (b) probably not (the free space is probably necessary to avoid block splits when the entries are inserted again - e.g. a *non-queue* interface table that is inserted/processed hourly and emptied/inserted again - there are a few in the system).

Well, another problem is that the system is 9.2.0.6 ;)

About shrinking (coalescing) nightly (or even hourly) - that's ok for the queue tables that I have identified (that mostly belongs to part of the system I've designed), but the system is a legacy system, I know there are a lot of queue tables but they're not labeled as such... for AQ tables, your solution is perfect anyway (it's just a matter of mining the data dictionary to get all the sweepers - I'll go that way for them, so for AQ, problem solved).

OK

Kumar, November 26, 2005 - 6:15 am UTC

Hi Tom,
When will an index get into an unusable state??

Are there any specific database events which can render an
index as unusable??

Tom Kyte
November 26, 2005 - 12:30 pm UTC

indexes become unusable when something happens to the segment they are indexing.

Like dropping a partition from a partitioned table could make the global indexes on the table go "unusable" (they point to rows that no longer exist!) - for example.

Unusable index.

Giridhar, April 26, 2007 - 2:33 am UTC

Tom,
Can we add following reasons on when Index may be unusable:

1) When we do ALTER TABLE .. MOVE
2) During the sqlldr loading using DIRECT Path Loading

Thanks

Tom Kyte
April 26, 2007 - 11:55 am UTC

1) sure, that would definitely count as "something happening to the segment"

2) direct path doesn't set indexes unusable. it can leave unique indexes in a direct path load state if you have duplicates, but in general, it would be valid

More to Explore

Performance

Get all the information about database performance in the Database Performance guide.