Sunteți pe pagina 1din 4

Empty Blocks Affecting ROWNUM Performance

ROWNUM is a pseudo column which is used to uniquely assign numbers (1,2,3,4,5…)


to result rows as they are retrieved. In this blog post I will demonstrate a situation
where using a ROWNUM filter on a table which has lot of empty blocks can negatively
impact on the execution time.
The root cause is not the ROWNUM itself, however lot of people are surprised when
using a filter simple as ROWNUM <= 1 takes minutes to execute. Using ROWNUM <=
N as a predicate triggers the optimizer to use COUNT STOPKEY operation, meaning it
will stop the table scan once N rows are identified.

In the case of a full table scan all blocks below HWM are read. Oracle has no way to
determine which blocks contain rows and which don’t beforehand, all blocks will be
read until N (ROWNUM) rows are identified. If there are empty blocks, it will result in
many reads. That might be the case if you do lot of direct path inserts where the rows
are placed after HWM in new unformatted blocks.
Usually, Oracle will reuse empty blocks for regular inserts. However, if the data is
loaded by direct path then Oracle won’t reuse any of the empty blocks.

Test case.

create table t1 nologging tablespace arsov_tbs_ssd


1
as
2
select rownum id, mod(rownum,200) id2, rpad('x',1500,'x') str from dual connect by
3
rownum <= 1e6
4
/
5
insert /*+ append */ into t1 select * from t1;
6
commit;
7
insert /*+ append */ into t1 select * from t1;
8
commit;
9
10
insert /*+ append */ into t1
11
select -1 id, mod(rownum,200) id2, rpad('x',1500,'x') str from dual
12
/
13
commit;

Having T1 fully packed without any deletes ROWNUM <= 1 completes in less than a
second.
Once it finds one row which satisfies ROWNUM <= 1 it stops with execution, that's the
purpose of COUNT STOPKEY operation.

116:15:51 IARSOV@orcl11g > select count(*) from t1 where rownum <= 1;


2
3 COUNT(*)
4----------
5 1
6
7Elapsed: 00:00:00.00

Having lot of empty blocks from the beginning of the segment will result in longer
execution time due to the fact that Oracle will spend some time until it finds a block
with a row. We can see that after we delete all rows except the latest which was inserted
with APPEND hint.

1 16:15:55 IARSOV@orcl11g > delete t1 where id > 0;


2
3 4000000 rows deleted.
4
5 16:31:19 IARSOV@orcl11g > select count(*) from t1 where rownum <= 1;
6
7 COUNT(*)
8 ----------
9 1
10
11Elapsed: 00:00:17.22
12
13Execution Plan
14----------------------------------------------------------
15Plan hash value: 912793033
16
17--------------------------------------------------------------------
18| Id | Operation | Name | Rows | Cost (%CPU)| Time |
19--------------------------------------------------------------------
20| 0 | SELECT STATEMENT | | 1 | 271K (1)| 00:54:18 |
21| 1 | SORT AGGREGATE | | 1| | |
22|* 2 | COUNT STOPKEY | | | | |
23| 3 | TABLE ACCESS FULL| T1 | 1 | 271K (1)| 00:54:18 |
24--------------------------------------------------------------------
25
26Predicate Information (identified by operation id):
27---------------------------------------------------
28
29 2 - filter(ROWNUM<=1)
30
31Note
32-----
33 - dynamic sampling used for this statement (level=2)
34
35
36Statistics
37----------------------------------------------------------
38 0 recursive calls
39 0 db block gets
40 1000010 consistent gets
41 1000002 physical reads
42 0 redo size
43 526 bytes sent via SQL*Net to client
44 524 bytes received via SQL*Net from client
45 2 SQL*Net roundtrips to/from client
46 0 sorts (memory)
47 0 sorts (disk)
48 1 rows processed
49
5016:31:54 IARSOV@orcl11g >

It scanned all segment blocks, 1000002 physical reads is 7812mb (8k block size) and T1
segment is 7872mb in size (I previously flushed the buffer cache).
Further we can confirm what’s the data distribution between the blocks with
DBMS_SPACE.SPACE_USAGE procedure.

1 16:32:13 SYS@orcl11g AS SYSDBA> @dbms_space_usage arsov t1 table


2 ...
3 unformatted_blocks: 0
4 unformatted_bytes: 0
5 fs1_blocks (25%): 0
6 fs1_bytes (25%): 0
7 fs2_blocks (50%): 0
8 fs2_bytes (50%): 0
9 fs3_blocks (75%): 0
10fs3_bytes (75%): 0
11fs4_blocks (100%): 1000000
12fs4_bytes (100%): 8192000000
13full_blocks: 1
14full_bytes: 8192
15v_total_blocks: 1007616
16v_total_bytes: 8254390272
17
18PL/SQL procedure successfully completed.
We have 7812mb free space (fs4_blocks – number of blocks fully empty), the same
amount which was processed until a row was encountered.

This is expected behavior if lot of DELETE’s are performed followed by INSERT’s with
APPEND hint, in which case empty blocks are not re-used.
There are multiple approaches to release unused space, we can use online table
redefinition, alter table move, data pump export/import, segment shrink, etc.

S-ar putea să vă placă și