数据块是oracle最基本的读写单位,但用户所需要的数据,并不是整个块,而是块中的行,或列.

当用户发出SQL语句时,此语句被解析执行完毕,就开始了数据的抓取阶段,在此阶段,服务器进程会先将行所在的数据块从数据文件中读入buffer cache,这个过程叫做物理读.物理读,每读取一个块,就算一次物理读.

当块被送进buffer cache后,并不能立即将块传给用户,因为用户所需要的并不整个块,而是块中的行.从buffer cache的块中读取行的过程,就是逻辑读.

为了完成一次逻辑读,服务器进程先要在hash表中查找块所在的cache buffer链.找到之后,需要在这个链上加一个cache buffer chains 闩,加闩成功之后,就在这个链中寻找指定的块,并在块上加一个pin锁.并释放cache buffer chains闩.然后就可以访问块中的行.

服务器进程不会将块中所有满足条件的行一次取出,而是根据抓取的命令,每次取一定数量的行.这些行取出之后,会经由PGA传给客户端用户.行一旦从buffer cache中取出,会话要释放掉在块上所加的PIN.本次逻辑读就算结束.

如果还要再抓取块中剩余的行,服务器进程要再次申请获得cache bufffer链闩.再次在块上加PIN.这就算是另外一次逻辑读咯.也就是说,服务器进程每申请一次cache buffer链闩,就是一次逻辑读.而每次逻辑读所读取的行的数量,可以在抓取命令中进行设置.逻辑读和Cache buffer chains闩关系密切.

TOM曾有文章提到,进程每申请一次Cache buffer chains闩,就是一次逻辑读。但是,逻辑读并不等同于Cache buffer chains闩,每次逻辑读,在9i中至少需要获得两Cache buffer chains闩。逻辑读是指在Hash表中定位块的这个过程。

散列表(Hash table,也叫哈希表)

是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。

给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数。

--创建测试表

SQL> create table t (id int,name varchar2(10));

Table created.

--插入100行数据

SQL> begin

for i in 1..100 loop

insert into t values (i,'aaa');

end loop;

commit;

end;

/

PL/SQL procedure successfully completed.

--查看表中行分布

SQL> select bno,max(id),min(id)

from (select dbms_rowid.rowid_block_number(rowid) bno, id from t)

group by bno;

BNO      MAX(ID)    MIN(ID)

----------  ----------   ----------

91545     100        1

可以看到这个表的所有数据全部存放在91545块上

--查询一行观察执行计划统计信息

SQL> set autotrace trace stat;

SQL> select * from t where id <= 1;

Statistics

----------------------------------------------------------

28    recursive calls

0     db block gets

8     consistent gets --逻辑读为8

0     physical reads

0     redo size

590  bytes sent via SQL*Net to client

524  bytes received via SQL*Net from client

2     SQL*Net roundtrips to/from client

0    sorts (memory)

0    sorts (disk)

1 rows processed

--在执行一次

SQL> select * from t where id <= 1;

Statistics

----------------------------------------------------------

0     recursive calls

0     db block gets

4     consistent gets --逻辑读减小到4

0     physical reads

0     redo size

590  bytes sent via SQL*Net to client

524  bytes received via SQL*Net from client

2     SQL*Net roundtrips to/from client

0     sorts (memory)

0     sorts (disk)

1 rows processed

--把id换成1到15的值,逻辑读和上面结果一模一样,下面给出了id=12的比较

SQL> select * from t where id <= 12;

12 rows selected.

Statistics

----------------------------------------------------------

5     recursive calls

0     db block gets

8     consistent gets --逻辑读减小到8

0     physical reads

0     redo size

728  bytes sent via SQL*Net to client

524  bytes received via SQL*Net from client

2     SQL*Net roundtrips to/from client

0     sorts (memory)

0     sorts (disk)

12 rows processed

SQL> /

12 rows selected.

Statistics

----------------------------------------------------------

0     recursive calls

0     db block gets

4     consistent gets --逻辑读减小到4

0     physical reads

0     redo size

728  bytes sent via SQL*Net to client

524  bytes received via SQL*Net from client

2     SQL*Net roundtrips to/from client

0     sorts (memory)

0     sorts (disk)

12 rows processed

--当把id换成16时逻辑读多了1 bytes sent via SQL*Net to client也多了200多

SQL> select * from t where id <=16;

16 rows selected.

Statistics

----------------------------------------------------------

5     recursive calls

0     db block gets

9     consistent gets

0     physical reads

0     redo size

896   bytes sent via SQL*Net to client

535   bytes received via SQL*Net from client

3     SQL*Net roundtrips to/from client

0     sorts (memory)

0     sorts (disk)

16 rows processed

SQL> /

16 rows selected.

Statistics

----------------------------------------------------------

0     recursive calls

0     db block gets

5     consistent gets

0     physical reads

0     redo size

896   bytes sent via SQL*Net to client

535   bytes received via SQL*Net from client

3     SQL*Net roundtrips to/from client

0     sorts (memory)

0     sorts (disk)

16 rows processed

从上面的实验看出,在读取数据的时候,逻辑读的次数与读取的数据量有很大的关系,oracle是通过批读取数据行的,批越大逻辑读就越少,而且服务端和客户端交互的次数也越少,由网络传输的数据也可以减少,下面继续实验

--批默认值

SQL> show arraysize;

arraysize 15

--设置批大小为1

SQL> select * from t;

100 rows selected.

Statistics

----------------------------------------------------------

4      recursive calls

0      db block gets

57     consistent gets

0      physical reads

0      redo size

10946  bytes sent via SQL*Net to client

1063   bytes received via SQL*Net from client

51     SQL*Net roundtrips to/from client

0     sorts (memory)

0     sorts (disk)

100 rows processed

--设置批大小为100

SQL> set arraysize 100

SQL> select * from t;

100 rows selected.

Statistics

----------------------------------------------------------

0     recursive calls

0     db block gets

4     consistent gets

0     physical reads

0     redo size

1832  bytes sent via SQL*Net to client

524  bytes received via SQL*Net from client

2     SQL*Net roundtrips to/from client

0     sorts (memory)

0     sorts (disk)

100 rows processed

可以看出逻辑的相差了10多倍,而bytes sent via SQL*Net to client也相差了6倍左右,继续下面的实验

--插入数据

SQL> begin

for i in 501..2000 loop

insert into t values (i,'aaa');

end loop;

commit;

end;

/

PL/SQL procedure successfully completed.

--查看表中行分布

SQL> select bno,max(id),min(id),count(*) from (select dbms_rowid.rowid_block_number(rowid) bno, id from t) group by bno;

BNO     MAX(ID)   MIN(ID) COUNT(*)

---------- ---------- ---------- ----------

91545    567       1         567

91548    2000      1686     315

91546    1126      568      559

91547    1685      1127     559

这时候数据存入了4个快中,默认arraysize为15,那么从91545这个块查询的次数就是567/15=37.8 我们估算约38次,验证下

SQL> select * from t where rownum < 568 --读取91548块的所有行

567 rows selected.

Statistics

----------------------------------------------------------

0     recursive calls

0     db block gets

41    consistent gets --和估算相差很接近

0     physical reads

0     redo size

14781 bytes sent via SQL*Net to client

931   bytes received via SQL*Net from client

39    SQL*Net roundtrips to/from client

0     sorts (memory)

0     sorts (disk)

567 rows processed

比估算的多了3个逻辑读,如果将批大小改为567,让oracle一次性读出这个块上的所有的行,根据上面的结果推算应该有4个逻辑读,是否是这样?

SQL> set arraysize 567

SQL> /

567 rows selected.

Statistics

----------------------------------------------------------

0     recursive calls

0     db block gets

4     consistent gets

0     physical reads

0     redo size

7899  bytes sent via SQL*Net to client

524  bytes received via SQL*Net from client

2     SQL*Net roundtrips to/from client

0     sorts (memory)

0     sorts (disk)

567 rows processed

实验看出刚好为4个,那么这3个逻辑读究竟用在什么地方了呢?继续下面实验

--重建表别且插入数据

SQL> create table t1 (id int, name varchar2(10));

Table created.

SQL> insert into t1 values (1,'aaa');

1 row created.

SQL> commit;

Commit complete.

--查看行分布

SQL> select bno,max(id),min(id),count(*)

from (select dbms_rowid.rowid_block_number(rowid) bno, id from t1)

group by bno;

BNO     MAX(ID)  MIN(ID)    COUNT(*)

---------- ---------- ---------- ----------

91697    1         1          1

--查看区分布

SQL> select extent_id,file_id,block_id,blocks

from dba_extents

where segment_name='T1'

and owner='SYS';

EXTENT_ID  FILE_ID      BLOCK_ID  BLOCKS

----------    ----------   ----------    ----------

0           1            91696      8

--查看高水位点

SQL> select header_file,header_block

from dba_segments

where segment_name='T1'

and owner='SYS';

HEADER_FILE    HEADER_BLOCK

-----------       ------------

1                91696

SQL> alter system dump datafile 1 block 91696

在dump文件中找到Highwater:: 0x00416632行,查出高水位点

SQL> select dbms_utility.data_block_address_block(to_number('00416632','xxxxxxxx'))

from dual;

DBMS_UTILITY.DATA_BLOCK_ADDRESS_BLOCK(TO_NUMBER('00416632','XXXXXXXX'))

-----------------------------------------------------------------------

91698

得出91696为数据段头部分,91697   91698为数据块

--执行查询

SQL> select * from t1 where rownum=1;

Statistics

----------------------------------------------------------

0     recursive calls

0     db block gets

3     consistent gets  --逻辑读减小到3

0     physical reads

0     redo size

594   bytes sent via SQL*Net to client

524   bytes received via SQL*Net from client

2     SQL*Net roundtrips to/from client

0     sorts (memory)

0     sorts (disk)

1 rows processed

逻辑读是3,难道这里刚好读到了这3个块,我只是推测,最好能够通过实验证明下!!!!!!!!!!

将批大小设置的越高,速度就越快,否则,Oracle直接将它设置为一个最大的值,我们知道一个session连接到实例会为这个session分配PGA,当行从Buffercache中读出来后,会先缓存在PGA中(具体是在游标的运行时区),然后再传给客户端。如果批大小过大,在PGA、客户端占用的内存也会增大。而且,如果渐歇性的在网络上传输大量数据,对网络也会有一定影响。下面来观察一下批大小对PGA的影响:

--创建一个100多万数据的测试表

SQL> create table t2 as select * from dba_objects;

Table created.

SQL> insert into t2 select * from t2;

73884 rows created.

SQL> /

147768 rows created.

SQL> /

295536 rows created.

SQL> /

591072 rows created.

SQL> /

1182144 rows created.

SQL> commit;

Commit complete.

--退出会话重新登录下,查询下sid

SQL> select distinct sid from v$mystat;

SID

----------

140

--在另外一个会话查询sid=140分配的PGA

SQL> SELECT a.pga_used_mem "PGA Used", a.pga_alloc_mem "PGA Alloc",

a.pga_max_mem "PGA Max"

FROM v$process a, v$session b

WHERE a.addr = b.paddr

AND b.sid= 140;

PGA Used   PGA Alloc   PGA Max

----------    ----------    ----------

711016     1376576     1376576

--返回sid=140的会话执行下面过程

declare

type test is table of t2.object_id%type;

v_test test;

cursor c is select object_id from t2;

begin

open c;

loop

fetch c bulk collect into v_test limit 500; --通过游标将批大小限制到500

exit when c%notfound;

end loop;

dbms_output.put_line(c%rowcount);

close c;

end;

/

我的这个表数据有点大,执行时间比较长,在执行完成后重新查看PGA大小还是一样

--将批大小改为5000

declare

type test is table of t2.object_id%type;

v_test test;

cursor c is select object_id from t2;

begin

open c;

loop

fetch c bulk collect into v_test limit 5000; --通过游标将批大小限制到500

exit when c%notfound;

end loop;

dbms_output.put_line(c%rowcount);

close c;

end;

/

第一个执行花了很久很久。。。这一个大概2秒钟,执行速度非常快。

查看PGA使用如下。可以看到扩大了一倍多。

PGA Used    PGA Alloc    PGA Max

----------     ----------    ----------

1038872     2031936     2949440

上面的实验很好的证明了设置arrraysize可以降低逻辑读,使用它还需要根据实际情况进行设置。

oracle 优化逻辑读过高(实战详解:逻辑读与arraysize关系)相关推荐

  1. Oracle大型数据库系统在AIX/UNIX上的实战详解

    前言 风,紧, 夜,深沉, 剑,已出鞘, 影,飘然前行! 本书的立意和内容 在服务器领域,IBM p系列服务器与AIX操作系统毫无疑问是UNIX服务器领域中的佼佼者,它代表着UNIX深刻的技术内涵和广 ...

  2. 《oracle大型数据库系统在AIX/unix上的实战详解》讨论31: oracle、sybase 数据库的不同访问...

    <Oracle大型数据库系统在AIX/UNIX上的实战详解> 讨论31:  oracle.sybase 数据库的不同访问方式   文平. 用户来信要求更细节比较一下Oracle和sybas ...

  3. 《Java和Android开发实战详解》——1.2节Java基础知识

    本节书摘来自异步社区<Java和Android开发实战详解>一书中的第1章,第1.2节Java基础知识,作者 陈会安,更多章节内容可以访问云栖社区"异步社区"公众号查看 ...

  4. mysql order by 语句_Mysql优化order by语句的方法详解

    本篇文章我们将了解ORDER BY语句的优化,在此之前,你需要对索引有基本的了解,不了解的老少爷们可以先看一下我之前写过的索引相关文章.现在让我们开始吧. MySQL中的两种排序方式 1.通过有序索引 ...

  5. 视频教程-HTML5+CSS3项目实战详解-HTML5/CSS

    HTML5+CSS3项目实战详解 13年软件开发经验,设计开发30多个大型软件,涉及政府.银行.电信.能源等大型软件项目. 精通J2EE体系架构,熟练使用Struts.Spring.hibernate ...

  6. libraries 和android runtime之间的关系,《Android Studio应用开发实战详解》——第1章,第1.3节Android系统架构...

    本节书摘来自异步社区<Android Studio应用开发实战详解>一书中的第1章,第1.3节Android系统架构,作者 王翠萍,更多章节内容可以访问云栖社区"异步社区&quo ...

  7. SENet实战详解:使用SE-ReSNet50实现对植物幼苗的分类

    摘要 1.SENet概述 ​ Squeeze-and-Excitation Networks(简称 SENet)是 Momenta 胡杰团队(WMW)提出的新的网络结构,利用SENet,一举取得最后一 ...

  8. oracle中imp命令详解,ORACLE EXPDP IMPDP数据导入导出命令详解及同EXP IMP命令详细对照...

    ORACLE EXPDP IMPDP数据导入导出命令详解及同EXP IMP 命令详细对比 一.EXPDP IMPDP EXP IMP 可以实现 1.可以实现逻辑备份和逻辑恢复 2.可以在数据库用户之间 ...

  9. Android系统性能优化(60)---LeakCanary使用详解

    Android内存优化(六)LeakCanary使用详解 1.概述 如果使用MAT来分析内存问题,会有一些难度,并且效率也不是很高,对于一个内存泄漏问题,可能要进行多次排查和对比.  为了能够简单迅速 ...

最新文章

  1. 2018年终总结之AI领域开源框架汇总
  2. 开始研究JavaScript
  3. JSP慕课网阶段用户登录小例子(不用数据库)
  4. SAP编程中最基本的概念
  5. tensorflow之读取jpg图像保存为tfrecord再读取
  6. 抢红包案例分析附代码实现(一)
  7. 荣之学:关于跨境电商shopee平台,你了解多少?
  8. Java 分布式面试题集合
  9. qq邮箱发送邮件服务器类型,设置QQ邮箱为发送邮件服务器的详细带图步骤
  10. [RK3399][Android7.1] 调试笔记 --- HDMI输出没有音频信号
  11. 《深入理解java虚拟机》学习-第三章-内存分配策略
  12. 青云科技上市:云计算企业的另一种最优解
  13. 软件设计师---程序设计语言
  14. 前景广阔的技术---云计算
  15. 超级简单易懂的蓝桥杯《成绩统计》解法
  16. 小陈java学习笔记0803
  17. [BZOJ]4491: 我也不知道题目名字是什么 线段树(差分)
  18. matlab 无法连线,MATLAB添加工具箱及无法连接到MathWorks问题
  19. A44-网页前端第四次笔记
  20. sal2edge.py显著性mask生成边缘标签

热门文章

  1. navicat自动备份和还原mysql数据库
  2. C. Tic-tac-toe
  3. 解决Linux系统yum下载缓慢的问题
  4. mysql启动之 Starting MySQL.. ERROR The server quit without updating PID file xs43254219216.pid
  5. 一键复制文本实现(navigator.clipboard 和 execCommand(废弃))
  6. Lambda表达式从入门到玩嗨儿~
  7. 【软件测试】自动化测试战零基础教程——Python自动化从入门到实战(八)
  8. 三分钟看出你适不适合开店!
  9. 页面水印添加工具【watermark-plus】,可防止手动删除水印,支持文本水印、图片水印、定制水印内容
  10. echart 入门小案例