db_file_multiblock_read_count
数据库引擎在多块读取期间(例如,全表扫描或索引快速全扫描)使用的最大磁盘I/O大小是由db_block_size和db_file_multiblock_read_count初始化参数值的乘积决定的。因此,在多块读取期间读取的最大块数量是由最大磁盘I/O大小除以读取的表空间的块大小来决定的。换句话说,对于默认块大小,db_file_multiblock_read_count初始化参数指定的是读取的最大块数量。这里仅指最大的数量是因为,至少有以下三种常见情况会导致多块读取的数量要小于该初始化参数指定的值。
Ø 对于段头块和其他只包含像扩展映射这样的段元数据的块,都是通过单块读取的。
Ø 物理读从来不会横跨多个扩展,但有一个例外,就是针对使用自动段空间管理的表空间执行直接路径读。
Ø 已经在缓冲区中的数据块,除非是直接路径读,否则不会从磁盘I/O子系统重新读取。
举例说明,图9-2展示了在使用手工段空间管理的表空间中存储的段的结构。与其他任何段一样,它由扩展组成(在本例中有2个),每一个扩展都是由块组成的(在本例中有16个)。第一个扩展的第一个块是段头。某些块(4、9、10、19和21)已经缓存在缓冲区中。为这个段执行缓冲读的数据库引擎进程无法执行任何的物理多块读,即使db_file_multiblock_read_count初始化参数设置为大于或等于32的值也不行。
如果将db_file_multiblock_read_count初始化参数设置为8,则会执行下面这些缓冲读。
Ø 一次段头的单块读(块1)。
Ø 一次两个块的多块读(块2和块3)。因为块4已经缓存所以无法读取更多的块。
Ø 一次四个块的多块读(从块5到块8)。因为块9已经缓存所以无法读取更多的块。
Ø 一次六个块的多块读(从块11到块16)。因为块16是该扩展的最后一个块,所以无法读取更多的块。
Ø 一次两个块的多块读(块17和块18)。因为块19已经缓存所以无法读取更多的块。
Ø 一次块20的单块读。因为块21已经缓存所以无法读取更多的块。
Ø 一次八个块的多块读(从块22到块29)。因为db_file_multiblock_read_count初始化参数被设置为8,所以无法读取更多的块。
Ø 一次三个块的多块读(从块30到块32)。
概括起来,这个进程执行了两次单块读操作和6次多块读操作。一次多块读读取的平均块数量大概是4个。平均大小小于8的事实解释了为何Oracle会在系统统计信息中引入mbrc值。
db_file_multiblock_read_count初始化参数是动态的,并且可以在实例和会话级别修改。在12.1多租户环境下,也可以在PDB级别设置它。
这时候,讨论一下查询优化器是如何计算多块读操作(例如,全表扫描或索引快速全扫描)的成本也非常重要。
当有负载系统统计信息可用时,
I/O成本并不依赖于db_file_multiblock_read_count
初始化参数的值。它是由公式9-1计算而来。注意,之所以用mreadtim除以sreadtim是因为查询优化器根据单块读正 常化了成本,就像在第7章中讨论的那样(公式7-2)。
公式9-1 使用有负载统计信息时多块读操作的I/O成本
在公式9-1中,若使用无负载统计信息,则变量会替换成以下值。
Ø 倘若db_file_multiblock_read_count初始化参数明确设置了,则mbrc由db_file_multiblock_read_count初始化参数的值替换;否则,使用8作为值。
Ø sreadtim 由公式7-3计算出来的值计算。
Ø mreadtim 由公式7-4计算出来的值计算。
这意味着只有在使用无负载统计信息时,db_file_multiblock_read_count初始化参数才会对多块读操作的成本产生直接的影响。这还意味着太高的值可能会导致过多的全表扫描或至少造成对多块读操作成本的低估。进一步讲,这是有负载统计信息优于无负载统计信息的另一种情况。
你已经知道了成本公式,现在需要知道如何找出db_file_multiblock_read_count初始化参数应该设置的值。最重要的是要认识到多块读对于性能有重大影响。因此,要小心设置db_file_multiblock_read_count初始化参数的值以达到最佳性能。虽然那些能够引发1MB磁盘I/O大小的值通常提供近乎最好的性能,但有时高一些或低一些的值会更好。
此外,更高的值通常需要更少的CPU来处理磁盘I/O操作。在不同的参数值下执行一个简单的全表扫描,可以给出关于这个初始化参数的影响的有用信息,进而帮助我们找到最佳值。下面的PL/SQL代码段是assess_dbfmbrc.sql脚本的一段摘录,可以用于此用途:
DECLARE
l_count PLS_INTEGER;
l_time NUMBER(10,1);
l_starting_time PLS_INTEGER;
l_ending_time PLS_INTEGER;
l_blocks PLS_INTEGER;
l_starting_blocks PLS_INTEGER;
l_ending_blocks PLS_INTEGER;
l_starting_cpu PLS_INTEGER;
l_ending_cpu PLS_INTEGER;
l_cpu PLS_INTEGER;
l_dbfmbrc PLS_INTEGER;
BEGIN
dbms_output.put_line('dbfmbrc blocks seconds cpu');
FOR
i IN 0..10
LOOP
l_dbfmbrc := power(2,i);
EXECUTE IMMEDIATE 'ALTER SESSION SET db_file_multiblock_read_count =
'||l_dbfmbrc;
EXECUTE IMMEDIATE 'ALTER SYSTEM FLUSH BUFFER_CACHE';
SELECT sum(decode(name, 'physical reads', value)),
sum(decode(name, 'CPU used by this session', value))
INTO l_starting_blocks, l_starting_cpu
FROM v$mystat ms JOIN v$statname USING (statistic#)
WHERE name IN ('physical reads','CPU used by this session');
l_starting_time := dbms_utility.get_time();
SELECT count(*) INTO l_count FROM &&owner . &&table_name
t;
l_ending_time := dbms_utility.get_time();
SELECT sum(decode(name, 'physical reads', value)),
sum(decode(name, 'CPU used by this session', value))
INTO l_ending_blocks, l_ending_cpu
FROM v$mystat ms JOIN v$statname USING (statistic#)
WHERE name IN ('physical reads','CPU used by this session');
l_time := round((l_ending_time-l_starting_time)/100,1);
l_blocks := l_ending_blocks-l_starting_blocks;
l_cpu := l_ending_cpu-l_starting_cpu;
dbms_output.put_line(l_dbfmbrc||' '||l_blocks||' '||to_char(l_time)||'
'||to_char(l_cpu));
END
LOOP;
END;
/
如你所见,实现起来也没有那么难。无论如何,当心不要在操作系统和磁盘I/O子系统级别缓存测试表,因为那样会导致测试失效。避免这样做的最简单方式是使用比你系统中可用的最大缓冲区还要大的表。对于预计要使用并行处理的系统,也值得去扩展这样的一个测试来执行并行查询。
图9-3展示了在我的测试系统上所有初始化参数设置为默认值的情况下,针对一个11.2的数据库执行以上的PL/SQL代码块测量得到的特征值。下面是需要注意的特征。
Ø 吞吐率由db_file_multiblock_read_count取较小值时的200MB/s增加到使用很大值时的600MB/s。
Ø CPU使用率从db_file_multiblock_read_count初始化参数取较小值时的1.5秒下降到取很大值时的0.5秒。
也可以让数据库引擎自动配置db_file_multiblock_read_count初始化参数的值。要使用这个特性,只需不设置它就可以了。如公式9-2所示,接下来数据库引擎就会尝试将其设置为一个能够允许1MB物理读的值。然而,不管怎样,如果缓冲区的大小与数据库支持的会话数量相比非常小,就会应用某种合理性检查以减小这个值。
公式9-2
db_file_multiblock_read_count 初始化参数的默认值
正如之前描述的那样,1MB的物理读并不总是最佳选择,所以建议不要使用这个特性。最好能够具体问题具体分析以找出最合适的值。
要知道如果将无负载统计信息与这个自动配置一起使用,mbrc就不会被公式9-1自动配置的值取代,而是会使用8这个值。