db_file_multiblock_read_count详解(原创)

db_file_multiblock_read_count
初始化参数db_file_multiblock_read_count 影响Oracle在执行连续的数据库扫描时,一次I/O允许读取的最大数据块数,这个参数的设置可能影响到CBO的执行计划选择。但db_file_multiblock_read_count的设置要受系统最大IO能力影响,也就是说,如果你系统的硬件IO能力有限,即使设置再大的db_file_multiblock_read_count也是没有用。
理论上,最大db_file_multiblock_read_count和系统IO能力有如下关系:
Max(db_file_multiblock_read_count) = MaxOsIOsize/db_block_size
当然这个Max(db_file_multiblock_read_count)还要受Oracle的限制,目前Oracle所支持的最大db_file_multiblock_read_count 值为128.
在Oracle10gR2之后的版本(10gR2和11g)中,Oracle数据库已经可以根据系统的IO能力以及Buffer Cache的大小来动态调整该参数值,Oracle建议不要显式设置该参数值。
为什么物理读不和理论值不一样
理论上I/O次数应为
表大小/(db_file_multiblock_read_count*db_block_size)
然而实际上I/O既有可能大于这个值,也有可能小于它,下面为实验过程

SQL> create table t (x int,y int);  
Table created.
SQL> insert into t values(1,1);
1 row created.
SQL> insert into t values(2,2);
1 row created.
将每个数据块的记录收缩到最小,这里每个数据块将只存储两行数据。
SQL> alter table t minimize records_per_block;
Table altered.
SQL> insert into t select rownum+2,1 from dba_objects where rownum<=254;
254 rows created.
SQL> create index t_idx on t(x);
Index created.
SQL> exec dbms_stats.gather_table_stats('HR','T');
PL/SQL procedure successfully completed.
查询数据的分布情况,这里数据共分布在128个数据块中
SQL> select count(distinct dbms_rowid.rowid_block_number(rowid)) from t;
COUNT(DISTINCTDBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID))
---------------------------------------------------
                                                128
SQL> alter system flush buffer_cache;
System altered.
SQL> show parameter multiblock           
NAME                                 TYPE                              VALUE
------------------------------------ --------------------------------- ------------------------------
db_file_multiblock_read_count        integer                           16
将数据块间隔的读入内存中
SQL> declare
  2   l_y number;
  3  begin
  4   for i in 1..64
  5   loop
  6   select y into l_y from t where x=i*4;
  7   end loop;
  8  end;
  9  /
PL/SQL procedure successfully completed.
SQL> alter session set events '10046 trace name context forever,level 12';
Session altered.
SQL> set autotrace traceonly statistics
可以看到一次全表扫描需要103次的物理读
SQL> select * from t;
256 rows selected.
Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
        179  consistent gets
        103  physical reads
          0  redo size
       4818  bytes sent via SQL*Net to client
        572  bytes received via SQL*Net from client
         19  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
        256  rows processed
查看10046产生的trace文件,可以看到每个wait#等待事件中,blocks都等于1,说明每次只读取一个数据块
SQL> !grep sequential /u01/app/admin/orcl/udump/orcl_ora_3535.trc
WAIT #30: nam='db file sequential read' ela= 287 file#=1 block#=1778 blocks=1 obj#=53617 tim=1305732442708769
WAIT #32: nam='db file sequential read' ela= 90 file#=1 block#=51 blocks=1 obj#=53617 tim=1305732442741712
WAIT #32: nam='db file sequential read' ela= 19 file#=1 block#=38 blocks=1 obj#=53617 tim=1305732442741863
...............省略若干行.................
WAIT #32: nam='db file sequential read' ela= 84 file#=1 block#=181 blocks=1 obj#=53617 tim=1305732442809561
WAIT #33: nam='db file sequential read' ela= 81 file#=1 block#=27267 blocks=1 obj#=53617 tim=1305732442813358

经上例可以知道,当没有连续的数据块可供读取时,无论db_file_multiblock_read_count设为何值,每次也只能读取一个数据块。

db_file_multiblock_read_count对CBO是怎样的影响

由于多块读取只发生在FTS(Full Table Scan)和INDEX_FFS(Index Fast Full Scan)当db_file_multiblock_read_count的值越大时,FFS或者INDEX_FFS的成本也就越低,执行计划就越倾向于使用FTS或者INDEX_FFS,见下例

SQL> set autotrace trace exp
SQL> alter session set db_file_multiblock_read_count=4;
Session altered.
SQL> select * from hr.t;
Execution Plan
----------------------------------------------------------
Plan hash value: 1601196873
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |   256 |  1536 |    69   (0)| 00:00:01 |
|   1 |  TABLE ACCESS FULL| T    |   256 |  1536 |    69   (0)| 00:00:01 |
--------------------------------------------------------------------------

SQL> alter system set db_file_multiblock_read_count=8;
Session altered.
SQL> select * from hr.t;
Execution Plan
----------------------------------------------------------
Plan hash value: 1601196873
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |   256 |  1536 |    51   (0)| 00:00:01 |
|   1 |  TABLE ACCESS FULL| T    |   256 |  1536 |    51   (0)| 00:00:01 |
--------------------------------------------------------------------------
SQL> alter system set db_file_multiblock_read_count=128;
Session altered.
SQL>  select * from hr.t;
Execution Plan
----------------------------------------------------------
Plan hash value: 1601196873
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |   256 |  1536 |    33   (0)| 00:00:01 |
|   1 |  TABLE ACCESS FULL| T    |   256 |  1536 |    33   (0)| 00:00:01 |
--------------------------------------------------------------------------
SQL> alter system set db_file_multiblock_read_count=129;
Session altered.
SQL>  select * from hr.t;
Execution Plan
----------------------------------------------------------
Plan hash value: 1601196873
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |   256 |  1536 |    33   (0)| 00:00:01 |
|   1 |  TABLE ACCESS FULL| T    |   256 |  1536 |    33   (0)| 00:00:01 |
--------------------------------------------------------------------------
SQL> show parameter multi
NAME                                 TYPE                              VALUE
------------------------------------ --------------------------------- ------------------------------
db_file_multiblock_read_count        integer                           128
SQL> show parameter db_block_size
NAME                                 TYPE                              VALUE
------------------------------------ --------------------------------- ------------------------------
db_block_size                        integer                           8192

可以看到,db_file_multiblock_read_count的值越大,在进行FTS时cost值越小,那为什么db_file_multiblock_read_count没有超过128呢,通常来讲,操作系统一次最大 I/O通常都是1M,那么1MB/db_block_size=max(db_file_multiblock_read_count),我的系统db_block_size是8k,故计算得max(db_file_multiblock_read_count)为128。有一种说法是受Oracle的限制,目前Oracle所支持的最大db_file_multiblock_read_count 值为128.这里笔者没有经过具体测试,也没见过官方的文档不敢下结论。读者可以自行在db_block_size为4k或者更小的数据库上进行测试。


参考至:《让Oracle跑得更快》谭怀远著

http://blog.chinaunix.net/uid-57865-id-2090667.html
http://www.eygle.com/archives/2009/03/db_file_multiblock_read_count_auto.html
本文原创,转载请注明出处、作者
如有错误,欢迎指正
邮箱:[email protected]

你可能感兴趣的:(count)