Tag Archives: multiblock reads

MULTIBLOCK READS AND CACHED BLOCKS

 

A full scan operation on a table reads multiple blocks in one I/O as opposed to single block reads which read only one block in one I/O.  The number of blocks read in one multiblock I/O   can range anywhere from one to the number of blocks specified in the db_file_multiblock_read_count parameter. For example, if the parameter is set to 64 and there are 640 blocks in the table,  the least no. of I/O’s required  to get all the blocks = 640/64 = 10. The no. of I/O’s could be more than one due  to the following limitations on multiblock read calls:

– Multiblock reads cannot span extent boundaries i.e.
if db_file_multiblock_read_count = 64 and extent size = 8 blocks, a multiblock read can read a maximum of 8 blocks only in one I/O.

– If a requested block is already in the buffer cache, it  will not be read again as part of the multiblock read.  Oracle will simply read the blocks up to those not already in memory, then issue another read call that skips those blocks to read the rest.  For example,
let’s say db_file_multiblock_read_count = 64 and
the range of blocks to be read is between block number 1 and 64. If block no. 32 is already available in the buffer cache, first multiblock read  reads would be done for blocks 1 to 31 and subsequent read will read blocks from block 33 to 64.

• Multiblock reads cannot  exceed the operating system limit for multiblock read size

In an earlier post, I had  demonstrated that multiblock reads cannot span extent boundaries.
In this post, I will demonstrate that multiblock reads skip blocks which are already cached.

Overview:

– Db_block_size = 8k
– create table MBRC_CACHE with uniform extent size = 8
– Populate table with records for id = 1 to 60 such that each block contains records for one id only
– Create index on the table on id column
– Trace the following
. Read records for id = 2
. Perform full scan on the table

– Trace file shows that
to read records for id = 2
. Index segment header block is read
. Index leaf blocks are read
. Table block containing records for id = 2 is read

to perform FTS on the table
. Table segment header block is read
. Table block containing records for id =1 is read
. Table block containing records for id = 2 is skipped
. Remaining Table blocks containing records for id = 3 to 60 are read

Implementation:

>SQL> sho parameter db_block_size

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
db_block_size integer 8192

SQL> sho parameter db_file_multi

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
db_file_multiblock_read_count integer 8

SQL> sho parameter optimizer_mode

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
optimizer_mode string ALL_ROWS<

- create table MBRC_CACHE with uniform extent size = 8

SQL> drop table mbrc_cache purge;
        create table mbrc_cache (id number , text char(700));

- Populate table with records for id = 1 to 60 such that each block contains records for one id only

begin
for i in 1..60 loop
for j in 1..10 loop
insert into mbrc_cache values (i, 'txt'||j);
commit;
end loop;
commit;
end loop;
end;
/

– check that records for each id are placed in one block each

SQL>select id, dbms_rowid.rowid_block_number(rowid),count(*)
from mbrc_cache
group by id, dbms_rowid.rowid_block_number(rowid)
order by id, dbms_rowid.rowid_block_number(rowid);

ID DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)   COUNT(*)
---------- ------------------------------------ ----------
1                                88809         10
2                                88810         10
3                                88811         10
4                                88812         10
5                                88813         10
6                                88814         10
7                                88815         10
8                                88816         10
9                                88817         10
10                                88818         10
11                                88819         10

...
56                                88872         10
57                                88873         10
58                                88874         10
59                                88875         10
60                                88876         10

- Create index on the table on id column and gather statistics

SQL>create index mbrc_cache_idx on mbrc_cache(id);

exec dbms_stats.gather_table_stats(USER, 'MBRC_CACHE', cascade=> true);

select table_name, num_rows, blocks from user_tables where table_name= 'MBRC_CACHE';

TABLE_NAME                       NUM_ROWS     BLOCKS
------------------------------ ---------- ----------
MBRC_CACHE                            600         60

– Find out object_id for mbrc_cache table and mbrc_)cache_idx index

SQL>col object_name for a30
select object_name, object_id from dba_objects where object_name like 'MBRC_CACHE%';

OBJECT_NAME                     OBJECT_ID
------------------------------ ----------
MBRC_CACHE                          75140
MBRC_CACHE_IDX                      75225

– Note that for table MBRC_CACHE, segment header is in block# 88808
— Note that for index MBRC_CACHE_IDX, segment header is in block# 88880

SQL> select segment_name, header_block from dba_segments where segment_name LIKE 'MBRC_CACHE%';

SEGMENT_NAME         HEADER_BLOCK
-------------------- ------------
MBRC_CACHE                  88808
MBRC_CACHE_IDX              88880

– Note that 8 blocks (88880 – 88888) are assigned to index mbrc_cache
— Block 88880 contains segment header
— Two index leaf blocks are located in block# 88881 and 88882

SQL> select SEGMENT_NAME, BLOCK_ID, BLOCKS from dba_extents where segment_name='MBRC_CACHE_IDX';

SEGMENT_NAME           BLOCK_ID     BLOCKS
——————– ———- ———-
MBRC_CACHE_IDX            88880          8

SQL> select index_name, leaf_blocks from user_indexes where index_name=’MBRC_CACHE_IDX';

INDEX_NAME                     LEAF_BLOCKS
—————————— ———–
MBRC_CACHE_IDX                           2

- Trace the following
. Read records for id = 2
. Perform full scan on the table

conn / as sysdba

alter system flush buffer_cache;
alter session set tracefile_identifier = 'mbrc';

set serveroutput off
exec dbms_monitor.session_trace_enable(waits=> true);
select * from mbrc_cache where id=2;
select * from mbrc_cache ;

declare
cursor c1_cur  is select * from mbrc_cache where id=2;
v1_cur mbrc_cache%rowtype;
cursor c2_cur  is select * from mbrc_cache;
v2_cur mbrc_cache%rowtype;
exec dbms_monitor.session_trace_disable();

begin
open c1_cur;
fetch c1_cur into v1_cur;

loop
fetch c1_cur into v1_cur;
exit when c1_cur%NOTFOUND;
end loop ;
close c1_cur;
end;
/
exec dbms_monitor.session_trace_disable();

SQL> col name for a15
col value for a60
select name, value from v$diag_info where upper(name) like '%TRACE F%';

NAME            VALUE
--------------- ------------------------------------------------------------
Default Trace F c:\app\administrator\diag\rdbms\orcl1\orcl1\trace\orcl1_ora_
ile             3828_mbrc.trc

– Read trace file

SQL> ho notepad c:\app\administrator\diag\rdbms\orcl1\orcl1\trace\orcl1_ora_3828_mbrc.trc

- Trace file (trimmed to show only read waits) shows that
to read records for id = 2
. Index segment header block is read
. Index leaf blocks are read
. Table block containing records for id = 2 is read

to perform FTS on the table
. Table segment header block is read
. Table block containing records for id =1 is read
. Table block containing records for id = 2 is skipped
. Remaining Table blocks containing records for id = 3 to 60 are read

=====================
PARSING IN CURSOR #1 len=39 dep=0 uid=0 oct=3 lid=0 tim=283016701180 hv=2279949904 ad=’7ff0da36078′ sqlid=’42bhbr63yajkh’
select * from mbrc_cache where id=2
END OF STMT
PARSE #1:c=0,e=1668,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=1,plh=3589681151,tim=283016701177
EXEC #1:c=0,e=55,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,plh=3589681151,tim=283016701337

– Read index mbrc_cache_idx (object_id  =75225 )
— Read two index leaf blocks  88881 and 88882 (as found earlier)

WAIT #1: nam=’db file sequential read’ ela= 1045 file#=1 block#=88881 blocks=1 obj#=75225 tim=283016702567
WAIT #1: nam=’db file sequential read’ ela= 20967 file#=1 block#=88882 blocks=1 obj#=75225 tim=283016723719

– Read table mbrc_cache (object_id = 75140)
— Read block 88810 contaning records for id = 2 (as found earlier)

WAIT #1: nam=’db file sequential read’ ela= 14929 file#=1 block#=88810 blocks=1 obj#=75140 tim=283016738901

WAIT #1: nam=’SQL*Net message from client’ ela= 72910 driver id=1111838976 #bytes=1 p3=0 obj#=75140 tim=283016812709
CLOSE #1:c=0,e=32,dep=0,type=0,tim=283016812906
=====================

PARSING IN CURSOR #2 len=29 dep=0 uid=0 oct=3 lid=0 tim=283016814397 hv=4285579899 ad=’7ff097aab18′ sqlid=’1qvcxnbzr1hmv’
select * from mbrc_cache
END OF STMT
PARSE #2:c=0,e=1425,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=1,plh=2256522414,tim=283016814394
EXEC #2:c=0,e=46,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,plh=2256522414,tim=283016814549

– During FTS, read segment header for table mbrc_cache (header block# = 88808 as found earlier)

WAIT #2: nam=’db file sequential read’ ela= 1843 file#=1 block#=88808 blocks=1 obj#=75140 tim=283016816604

– Read block 88809 containing rows for id = 1 ( as found earlier)

WAIT #2: nam=’db file sequential read’ ela= 384 file#=1 block#=88809 blocks=1 obj#=75140 tim=283016817170

– Skip block 88810 containing records for id=2 as that block has already been read into buffer
— Read 5 remaining blocks in the extent starting from block 88811

WAIT #2: nam=’db file scattered read’ ela= 22144 file#=1 block#=88811 blocks=5 obj#=75140 tim=283016947620

– Read subdsequent extents in units of 8 blocks

WAIT #2: nam=’db file scattered read’ ela= 302 file#=1 block#=88816 blocks=8 obj#=75140 tim=283017221664

WAIT #2: nam=’db file scattered read’ ela= 33 file#=1 block#=88824 blocks=8 obj#=75140 tim=283017573336

WAIT #2: nam=’db file scattered read’ ela= 17389 file#=1 block#=88832 blocks=8 obj#=75140 tim=283017918715

WAIT #2: nam=’db file scattered read’ ela= 496 file#=1 block#=88840 blocks=8 obj#=75140 tim=283018212031

WAIT #2: nam=’db file scattered read’ ela= 28 file#=1 block#=88848 blocks=8 obj#=75140 tim=283018477971

WAIT #2: nam=’db file scattered read’ ela= 31 file#=1 block#=88856 blocks=8 obj#=75140 tim=283018786018

– Read 5 blocks  from last extent
— Last block 88876 contains records for id = 60 (as found earlier)
— Read 4 blocks ( 88872 – 88876) containing data
— Read one more block to ensure that there are no more blocks containing data

WAIT #2: nam=’db file scattered read’ ela= 28 file#=1 block#=88872 blocks=5 obj#=75140 tim=283019045215

WAIT #2: nam=’SQL*Net message from client’ ela= 54378 driver id=1111838976 #bytes=1 p3=0 obj#=75140 tim=283019256210
CLOSE #2:c=0,e=25,dep=0,type=0,tim=283019260582

=====================

Conclusion:

– If a requested block is already in the buffer cache, it  will not be read again as part of the multiblock read.  Oracle will simply read the blocks up to those not already in memory, then issue another read call that skips those blocks to read the rest.

References:

Pro Oracle SQL by Karen Morton, Kerry Osborne,  Robyn Sands, Riyaz and Jared Still

Oracle Documentation

————————————————————————————————-

Related links:

Home

Tuning Index

Oracle always full scans tables smaller than DBFMRC:  A myth

DB_FILE_MULTIBLOCK_READ_COUNT And Extent Size

 

——————–

 

DB_FILE_MULTIBLOCK_READ_COUNT AND EXTENT SIZE

A full scan operation on a table reads multiple blocks in one I/O as opposed to single block reads which read only one block in one I/O. The number of blocks read in one multiblock I/O can range anywhere from one to the number of blocks specified in the db_file_multiblock_read_count parameter. For example, if the parameter is set to 64 and there are 640 blocks in the table, the least no. of I/O’s required to get all the blocks = 640/64 = 10.

The no. of I/O’s could be more than one due to the following limitations on multiblock read calls:

– Multiblock reads cannot span extent boundaries i.e.
if db_file_multiblock_read_count = 64 and extent size = 8 blocks, a multiblock read can read a maximum of 8 blocks only in one I/O.

– If a requested block is already in the buffer cache, it will not be read again as part of the multiblock read. Oracle will simply read the blocks up to those not already in memory, then issue another read call that skips those blocks to read the rest. For example,
let’s say db_file_multiblock_read_count = 64 and
the range of blocks to be read is between block number 1 and 64. If block no. 32 is already available in the buffer cache, first multiblock read reads would be done for blocks 1 to 31 and subsequent read will read blocks from block 33 to 64.

• Multiblock reads cannot exceed the operating system limit for multiblock read size

In this post, I will demonstrate that multiblock reads cannot span extent boundaries.

In my next post, I will demonstrate that multiblock reads skip the bocks which already cached.

 

Overview:

– Db_block_size = 8k
– Set db_file_multiblock_read_count = 64
– create two tables with identical data :
. table mbrc_ext_64k with uniform extent size = 8
. table mbrc_ext_512k with uniform extent size = 64
. table mbrc_ext_1024k with uniform extent size = 128

– Trace FTS on both the tables and check the trace file
– Trace file shows that
. I/O’s made to mbrc_ext_64k are of size 8 blocks (=extent size)
. I/O’s made to mbrc_ext_512k are of size 64 blocks(= db_file_multiblock_read_count )
. I/O’s made to mbrc_ext_1024k are of size 64 blocks(= db_file_multiblock_read_count )

Implementation:

– check that db_block_size = 8k

SQL> sho parameter db_block_size

NAME TYPE VALUE
------------------------------------ ----------- -----------------
db_block_size integer 8192

— Set db_file_multiblock_read_count = 64

SQL> alter system set db_file_multiblock_read_count =64
sho parameter db_file_multi

NAME TYPE VALUE
------------------------------------ ----------- -----------------
db_file_multiblock_read_count integer 64

— check that optimizer_mode = all_rows

SQL> sho parameter optimizer_mode

NAME TYPE VALUE
------------------------------------ ----------- -----------------
optimizer_mode string ALL_ROWS

— Create tablespaces
. tbs_64k – Uniform Extent size = 64K = 8 blocks
. tbs_512k – Uniform Extent size = 512K = 64 blocks
. tbs_1024k- Uniform Extent size = 512K = 128 blocks

SQL>drop tablespace tbs_64k including contents and datafiles;
create tablespace tbs_64k datafile 'C:\APP\mbrc8k.dbf' size 100m reuse autoextend on
extent management local uniform size 64k segment space management manual;

drop tablespace tbs_512k including contents and datafiles;
create tablespace tbs_512k datafile 'C:\APP\mbrc512k.dbf' size 100m reuse autoextend on
extent management local uniform size 512k segment space management manual;

drop tablespace tbs_1024k including contents and datafiles;
create tablespace tbs_1024k datafile 'C:\APP\mbrc1024k.dbf' size 100m reuse autoextend on
extent management local uniform size 1024k segment space management manual;

– create three tables with identical data :
. table mbrc_ext_64k in tbs_64k with uniform extent size = 8 blocks
. table mbrc_ext_512k in tbs_512k with uniform extent size = 64 blocks
. table mbrc_ext_1024k in tbs_1024k with uniform extent size = 128 blocks

drop table mbrc_ext_64k purge;
create table mbrc_ext_64k tablespace tbs_64k as select * from sh.sales where rownum<=50000;

drop table mbrc_ext_512k purge;
create table mbrc_ext_512k tablespace tbs_512k as select * from sh.sales where rownum<=50000;

drop table mbrc_ext_1024k purge;
create table mbrc_ext_1024k tablespace tbs_1024k as select * from sh.sales where rownum<=50000;

— Gather statistics

exec dbms_stats.gather_table_stats(USER, 'MBRC_EXT_64K', cascade=> true);
exec dbms_stats.gather_table_stats(USER, 'MBRC_EXT_512K', cascade=> true);
exec dbms_stats.gather_table_stats(USER, 'MBRC_EXT_1024K', cascade=> true);

— check that extent size for
. MBRC_EXT_64K = 8
. MBRC_EXT_512K = 64
. MBRC_EXT_512K = 128

SQL>col segment_name for a20
select segment_name, blocks, count(*)
from dba_extents
where segment_name like 'MBRC_EXT_%K'
group by segment_name, blocks;

SEGMENT_NAME BLOCKS COUNT(*)
-------------------- ---------- ----------
MBRC_EXT_1024K 128 2
MBRC_EXT_64K 8 31
MBRC_EXT_512K 64 4

— Find out object_id’s of the tables

SQL>col object_name for a30
select object_name, object_id from dba_objects where object_name like 'MBRC_EXT_%K%';

OBJECT_NAME OBJECT_ID
------------------------------ ----------
MBRC_EXT_1024K 75139
MBRC_EXT_512K 75131
MBRC_EXT_64K 75130

— Enable tracing and per Full table scan of the tables

conn / as sysdba

alter system flush buffer_cache;
alter session set tracefile_identifier = 'mbrc';

set serveroutput off
exec dbms_monitor.session_trace_enable(waits=> true);

declare
cursor c1_cur is select * from mbrc_ext_64k;
cursor c2_cur is select * from mbrc_ext_512k;
cursor c3_cur is select * from mbrc_ext_1024k;
v1_cur mbrc_ext_64k%rowtype;
v2_cur mbrc_ext_512k%rowtype;
v3_cur mbrc_ext_1024k%rowtype;

begin
open c1_cur;
loop
fetch c1_cur into v1_cur;
exit when c1_cur%NOTFOUND;
end loop ;
close c1_cur;

open c2_cur;
loop
fetch c2_cur into v2_cur;
exit when c2_cur%NOTFOUND;
end loop ;
close c2_cur;

open c3_cur;
loop
fetch c3_cur into v3_cur;
exit when c3_cur%NOTFOUND;
end loop ;
close c3_cur;

end;
/
exec dbms_monitor.session_trace_disable();

— Find out name of trace file generated

SQL>col value for a80
select name, value from v$diag_info where upper(name) like '%TRACE F%';

NAME VALUE
-------------------- -------------------------------------------------------------------------
Default Trace File c:\app\administrator\diag\rdbms\orcl1\orcl1\trace\orcl1_ora_5476_mbrc.trc

— Read the trace file

SQL>ho notepad c:\app\administrator\diag\rdbms\orcl1\orcl1\trace\orcl1_ora_5476_mbrc.trc

=====================

In MBRC_EXT_64K (object_id = 75130)

Single block read identified by wait event ‘db file sequential read’
. Reads block# 128

— Let’s verify that block# 128 contains segment header

SQL> select segment_name, header_block from dba_segments where segment_name = 'MBRC_EXT_64k';

SEGMENT_NAME HEADER_BLOCK
-------------------- ------------
MBRC_EXT_64K 128

First multiblock read (identified by wait event ‘db file scattered read’)
.starting at block# 129 (Block next to segment header)
.No. of blocks read = 7 (blocks=7= remainig blocks in the extent)

Subsequent multiblock reads (identified by wait event ‘db file scattered read’)
. from block# 136 (129+7) onwards
. No. of blocks read = 8 (blocks = 8)

Last multiblock read (identified by wait event ‘db file scattered read’)
. starting at block# 368
. No. of blocks read = 2 (blocks = 2 = remaining blocks containing data)

Last block# read = 368 + 2 (in last wait, starting block = 368 , blocks read = 2)
= 370
First block# read (after segment header) = 129

Total no. of blocks scanned = 370 – 129
= 241

— Let’s verify that this no. matches the No. of blocks containing data as stored in data dictionary

SQL>select table_name, blocks from user_tables where table_name = 'MBRC_EXT_64K';

TABLE_NAME BLOCKS
------------------------------ ----------
MBRC_EXT_64K 241

=====================

PARSING IN CURSOR #2 len=26 dep=1 uid=0 oct=3 lid=0 tim=249360274955 hv=1652364071 ad=’7ff09780df8′ sqlid=’d3ubygdj7u4t7′
SELECT * FROM MBRC_EXT_64K
END OF STMT

PARSE #2:c=0,e=141,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=1,plh=3512697650,tim=249360274953
EXEC #2:c=0,e=52,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=1,plh=3512697650,tim=249360275088

WAIT #2: nam=’Disk file operations I/O’ ela= 211 FileOperation=2 fileno=7 filetype=2 obj#=75130 tim=249360275427

WAIT #2: nam=‘db file sequential read’ ela= 22023 file#=7 block#=128 blocks=1 obj#=75130 tim=249360297493

WAIT #2: nam=‘db file scattered read’ ela= 1305 file#=7 block#=129 blocks=7 obj#=75130 tim=249360299150

WAIT #2: nam=‘db file scattered read‘ ela= 951 file#=7 block#=136 blocks=8 obj#=75130 tim=249360386033

WAIT #2: nam=‘db file scattered read’ ela= 478 file#=7 block#=144 blocks=8 obj#=75130 tim=249360487978

WAIT #2: nam=‘db file scattered read‘ ela= 459 file#=7 block#=152 blocks=8 obj#=75130 tim=249360584467
….


WAIT #2: nam=’db file scattered read‘ ela= 213 file#=7 block#=368 blocks=2 obj#=75130 tim=249362258692

STAT #2 id=1 cnt=50000 pid=0 pos=1 obj=75130 op=’TABLE ACCESS FULL MBRC_EXT_64K (cr=50006 pr=242 pw=0 time=251918 us cost=64 size=1450000 card=50000)’

CLOSE #2:c=0,e=4,dep=1,type=3,tim=249362277148
=====================

=====================

In MBRC_EXT_512K (object_id = 75131)

Single block read identified by wait event ‘db file sequential read’
. Reads block# 128

— Let’s verify that block# 128 contains segment header

SQL> select segment_name, header_block from dba_segments where segment_name = 'MBRC_EXT_512k';

SEGMENT_NAME HEADER_BLOCK
-------------------- ------------
MBRC_EXT_512K 128

First multiblock read (identified by wait event ‘db file scattered read’)
.starting at block# 129 (Block next to segment header)
.No. of blocks read = 63 (blocks=63 = remainig blocks in the extent)

Subsequent multiblock reads (identified by wait event ‘db file scattered read’)
. from block# 192 (129+63) onwards
. No. of blocks read = 64 (blocks = 64)

Last multiblock read (identified by wait event ‘db file scattered read’)
. starting at block# 320
. No. of blocks read = 50 (blocks = 50 = remaining blocks containing data)

Last block# read = 320 + 50 (in last wait, starting block = 320 , blocks read = 50)
= 370
First block# read (after segment header) = 129

Total no. of blocks scanned = 370 – 129
= 241

— Let’s verify that this no. matches the No. of blocks containing data as stored in data dictionary

SQL>select table_name, blocks from user_tables where table_name = 'MBRC_EXT_512K';

TABLE_NAME BLOCKS
------------------------------ ----------
MBRC_EXT_512K 241

=====================
PARSING IN CURSOR #2 len=27 dep=1 uid=0 oct=3 lid=0 tim=249362277315 hv=3723803804 ad=’7ff097d1ec8′ sqlid=’grmbfwvfz9g4w’
SELECT * FROM MBRC_EXT_512K
END OF STMT
PARSE #2:c=0,e=96,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=1,plh=748809365,tim=249362277313
EXEC #2:c=0,e=48,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=1,plh=748809365,tim=249362277446

WAIT #2: nam=’Disk file operations I/O’ ela= 191 FileOperation=2 fileno=8 filetype=2 obj#=75131 tim=249362277741

WAIT #2: nam=’db file sequential read‘ ela= 18302 file#=8 block#=128 blocks=1 obj#=75131 tim=249362296085

WAIT #2: nam=‘db file scattered read’ ela= 4297 file#=8 block#=129 blocks=63 obj#=75131 tim=249362300903

WAIT #2: nam=‘db file scattered read‘ ela= 2675 file#=8 block#=192 blocks=64 obj#=75131 tim=249362851282

WAIT #2: nam=‘db file scattered read‘ ela= 3372 file#=8 block#=256 blocks=64 obj#=75131 tim=249363235459

WAIT #2: nam=‘db file scattered read‘ ela= 2651 file#=8 block#=320 blocks=50 obj#=75131 tim=249363612810

STAT #2 id=1 cnt=50000 pid=0 pos=1 obj=75131 op=’TABLE ACCESS FULL MBRC_EXT_512K (cr=50003 pr=242 pw=0 time=192560 us cost=64 size=1450000 card=50000)’

CLOSE #2:c=0,e=2,dep=1,type=3,tim=249363956416
EXEC #1:c=3634823,e=3681748,p=484,cr=100009,cu=0,mis=0,r=1,dep=0,og=1,plh=0,tim=249363956444
WAIT #1: nam=’SQL*Net message to client’ ela= 3 driver id=1111838976 #bytes=1 p3=0 obj#=75131 tim=249363956494
WAIT #1: nam=’SQL*Net message from client’ ela= 2159 driver id=1111838976 #bytes=1 p3=0 obj#=75131 tim=249363958679
CLOSE #1:c=0,e=38,dep=0,type=0,tim=249363958919
=====================
=====================
=====================

In MBRC_EXT_1024K (object_id = 75139)

Single block read identified by wait event ‘db file sequential read’
. Reads block# 128

— Let’s verify that block# 128 contains segment header

SQL> select segment_name, header_block from dba_segments where segment_name = 'MBRC_EXT_1024k';

SEGMENT_NAME HEADER_BLOCK
-------------------- ------------
MBRC_EXT_1024K 128

First multiblock read (identified by wait event ‘db file scattered read’)
.starting at block# 129 (Block next to segment header)
.No. of blocks read = 64 (blocks=64 = db_file_multiblock_count since extent contains 128 blocks and there are still 127 blocks left in the extent)

Second multiblock read (identified by wait event ‘db file scattered read’)
.starting at block# 163 (129 + 64)
.No. of blocks read = 63 (blocks=63 = remaining blocks in the extent)

Subsequent multiblock reads (identified by wait event ‘db file scattered read’)
. from block# 256 (193 +63) onwards
. No. of blocks read = 64 (blocks = 64)

Last multiblock read (identified by wait event ‘db file scattered read’)
. starting at block# 320
. No. of blocks read = 50 (blocks = 50 = remaining blocks containing data)

Last block# read = 320 + 50 (in last wait, starting block = 320 , blocks read = 50)
= 370
First block# read (after segment header) = 129

Total no. of blocks scanned = 370 – 129
= 241

— Let’s verify that this no. matches the No. of blocks containing data as stored in data dictionary

SQL>select table_name, blocks from user_tables where table_name = 'MBRC_EXT_1024K';

TABLE_NAME BLOCKS
------------------------------ ----------
MBRC_EXT_1024K 241

==================================

PARSING IN CURSOR #2 len=28 dep=1 uid=0 oct=3 lid=0 tim=262780042519 hv=4010235831 ad=’7ff0d84e9e8′ sqlid=’7rs934mrhfpxr’
SELECT * FROM MBRC_EXT_1024K
END OF STMT

PARSE #2:c=15601,e=4201,p=0,cr=21,cu=0,mis=1,r=0,dep=1,og=1,plh=3425801585,tim=262780042518
EXEC #2:c=0,e=23,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=1,plh=3425801585,tim=262780042611
WAIT #2: nam=’Disk file operations I/O’ ela= 169 FileOperation=2 fileno=9 filetype=2 obj#=75139 tim=262780042872

WAIT #2: nam=‘db file sequential read‘ ela= 6916 file#=9 block#=128 blocks=1 obj#=75139 tim=262780049822

WAIT #2: nam=’db file scattered read‘ ela= 3837 file#=9 block#=129 blocks=64 obj#=75139 tim=262780053909

WAIT #2: nam=‘db file scattered read‘ ela= 2422 file#=9 block#=193 blocks=63 obj#=75139 tim=262780770697

WAIT #2: nam=’db file scattered read‘ ela= 2005 file#=9 block#=256 blocks=64 obj#=75139 tim=262781424869

WAIT #2: nam=db file scattered read‘ ela= 40794 file#=9 block#=320 blocks=50 obj#=75139 tim=262782155450

STAT #2 id=1 cnt=50000 pid=0 pos=1 obj=75139 op=’TABLE ACCESS FULL MBRC_EXT_1024K (cr=50003 pr=242 pw=0 time=274738 us cost=64 size=1450000 card=50000)’
CLOSE #2:c=0,e=3,dep=1,type=3,tim=262782568794
EXEC #1:c=7020045,e=7580902,p=732,cr=150081,cu=0,mis=0,r=1,dep=0,og=1,plh=0,tim=262782568832
WAIT #1: nam=’SQL*Net message to client’ ela= 5 driver id=1111838976 #bytes=1 p3=0 obj#=75139 tim=262782568901
WAIT #1: nam=’SQL*Net message from client’ ela= 2776 driver id=1111838976 #bytes=1 p3=0 obj#=75139 tim=262782571712
CLOSE #1:c=0,e=44,dep=0,type=0,tim=262782571842

=========================
Conclusion:

– During FTS, segment header is accessed using single block read first to get the extent map.
– During first multiblock read,
if remaining blocks in the extent < db_file_multiblock_read_count
remaining blocks in the first extent (excluding segmnet header block) are read.
else (remaining blocks in the extent >= db_file_multiblock_read_count
blocks = db_file_multiblock_read_count are read

– During subsequent multiblock reads,
if remaining blocks in the extent < db_file_multiblock_read_count
remaining blocks in the extent are read.
else (remaining blocks in the extent >= db_file_multiblock_read_count
blocks = db_file_multiblock_read_count are read

– During last multiblock read, remaining blocks containing data are read.

– If extent size <= db_file_multiblock_read_count
No. of blocks read in one I/O = No. of blocks in one extent

– If extent size > db_file_multiblock_read_count
No. of blocks read in one I/O = least (db_file_multiblock_read_count, no. of blocks containing data)

Hence, we have been able to verify that multiblock reads cannot span extents and maximum no. of blocks that can be read in one I/O = db_file_multiblock_read_count

References:

https://forums.oracle.com/forums/thread.jspa?messageID=11051591#11051591

———————————————————————————

Related links:

Home

Tuning Index
Multiblock Reads And Cached Blocks
Oracle Always Full Scans Tables Smaller Than DFMBRC: A Myth