1.1.1 误区
检查逻辑读数时,必须注意两个会曲解指标的误区。第一个与一致读有关,第二个与行预取有关。
1.一致读
对于每一条SQL语句,数据库引擎都会保证处理数据的一致性。为了达到这个目的,数据块的一致性副本会基于当前数据块和回滚块在运行时创建。要执行这样的操作需要完成数个逻辑读。因此,访问路径操作执行的逻辑读数非常依赖于需要重建的块数。
以下代码引用自read_consistency.sql脚本生成的输出。请注意使用的查询与上节相同。根据执行统计信息,会返回相同的行数(实际上返回相同的数据)。然而,它会执行更多的逻辑读(相比28,一共执行了354)。会造成这个影响是因为修改了数据块的另一个会话需要执行该查询。由于在查询开始时修改并未提交,数据库引擎就必须重建这些块。这会导致更高的逻辑读:
select * FROM t WHERE n1 BETWEEN 6000 AND
7000 AND n2 = 19;
2.行预取
从优化的角度来看,应该避免基于行的处理。例如,当客户端从数据库取回数据时,它可以逐行取回,或者更好些,一次取回多行。这个技术,被称为行预取(row prefetching)。现在,让我们只看它对逻辑读数的影响。
简单地说,每当数据库引擎访问一个块,逻辑读就会计数一次。针对全表扫描,会有两个极端。如果将行预取设置为1,返回每行大约一个逻辑读。如果将行预取设置为大于每个表块中存储的行数,那么逻辑读就接近表的块数。以下代码引用自row_prefetching.sql脚本生成的输出。在第一个执行中,行预取设置为2,逻辑读数(5388)大约是行数(10000)的一半。在第二个执行中,由于行预取数(100)高于每个块的平均行数(25),逻辑读数(488)大约等于块数(401):
select num_rows,blocks,round(num_rows/blocks)
AS rows_per_block
FROM user_tables
WHERE table_name='T';
Set arraysize 2
select * FROM t;
注意 在SQL*Plus中,通过arraysize系统变量管理行预取数。默认值是15。
考虑到行预取对逻辑读数的依赖,每当出于测试目的使用诸如SQL*Plus之类的工具执行SQL语句时,都应该仔细将行预取值设置得与应用一致。换句话说,用来测试的工具预取的行数应与应用一致。如果不这样做,会导致很多错误的结果。
当执行的操作被阻塞时(例如,聚合操作),SQL引擎会在内部使用行预取。结果,当聚合是执行计划的一部分时,访问路径的逻辑读数会非常接近块数。换句话说,无论行预取设置成什么,每次SQL引擎访问一个块,它都会包含所有行。下面举例说明:
set arraysize 2
select sum(n1) FROM t;