第一篇 《oracle等待事件1分别用表和索引上数据的访问来产生db file scattered read等待事件》http://leonarding.blog.51cto.com/6045525/1105411
第二篇 《oracle等待事件2构造一个DB File Sequential Read等待事件和构造一个Direct Path Read等待事件》http://leonarding.blog.51cto.com/6045525/1105414
第三篇 《oracle等待事件3构造一个Direct Path write等待事件和构造一个Log File Sync等待事件》http://leonarding.blog.51cto.com/6045525/1105416
三 构造一个DB File Sequential Read等待事件,等待事件需要在v$session_wait和10046 trace文件中显示出来,贴出整个演示过程。
db file sequential read 等待事件:是由于数据块顺序读产生的,当数据块(索引块)从磁盘一个一个读到内存中时,在这个过程中oracle会发生“db file sequential read” 等待事件。
块顺序读场景:索引块顺序读
数据块顺序读
undo回滚构造一致性读
磁盘I/O瓶颈
一般来讲如果检索数据时走索引范围扫描INDEX RANGE SCAN 就会发生数据块顺序读的现象,先读取一个索引块,根据索引键值对应ROWID信息在去读ROWID所在的数据块,接下来继续找下一个索引块,在读对应的数据块,就这样一个一个把数据块读取到内存中,这个过程中就会产生“db file sequential read” 等待事件。
下面我们来使用索引块顺序读来产生“db file sequential read”
准备环境,原本想利用上面的创建好的表和索引来做的,但一想为了给筒子们一个完整的,详细的,琐碎的,容易验的实验例子,我还是当作重新开始一步一步来搭建环境演示给大家。前提是不是要清理一下环境
LEO1@LEO1> drop table leo1 purge; 清理上次的环境
Table dropped.
LEO1@LEO1> create table leo1 as select * from dba_objects; 创建表
Table created.
LEO1@LEO1> create index idx_leo1 on leo1(object_id); object_id字段上创建索引,我们要利用索引块顺序读
Index created.
LEO1@LEO1> select table_name,index_name from user_indexes where table_name='LEO1';
TABLE_NAME INDEX_NAME
------------------------------ ------------------------------
LEO1 IDX_LEO1
LEO1@LEO1> execute dbms_stats.gather_table_stats(ownname=>'LEO1',tabname=>'LEO1',cascade=>TRUE);
PL/SQL procedure successfully completed. 为啥每次都需要分析表呢,收集表数据分布情况,看看有没有数据倾斜,在看看索引键值重复率,表和索引存放的位置和大小等,有了一些信息方便CBO更加客观评价执行计划,不做可以嘛?yes -> oracle在必要情况下会进行动态采集操作,显然动态采集只是尽可能抓取一部分样本数据来评估,没有前面一种客观和科学,它的好处在于自动化和方便,无需人工干预。
LEO1@LEO1> set autotrace trace exp 启动执行计划
LEO1@LEO1> select owner,object_id,object_name,object_type,status from leo1 where object_id>=100 and object_id<=200; oracle进行IRS扫描
Execution Plan
----------------------------------------------------------
Plan hash value: 1434365503
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 100 | 5200 | 4 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID | LEO1 | 100 | 5200 | 4 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IDX_LEO1 | 100 | | 2 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("OBJECT_ID">=100 AND "OBJECT_ID"<=200) 谓词条件是对*操作的说明
我们只看[100,之间的数据,走的是索引范围扫描,就是先读索引块,通过索引键值找到ROWID,在读数据块,这样一个一个读到内存中,就是数据块顺序读类型
LEO1@LEO1> set autotrace off
LEO1@LEO1> select sid,event,total_waits,time_waited from v$session_event;
SID EVENT TOTAL_WAITS TIME_WAITED
---------- --------------------------------------------------------------------------------
133 db file sequential read 470 122
我们先标记一下等待的次数,如果一会次数增加那么说明成功触发了这个等待事件
LEO1@LEO1> set serveroutput on
LEO1@LEO1> create or replace procedure p2
as
leo number;
begin
for i in 1..8000
loop
select count(*) into leo from leo1 where object_id>=100 and object_id<=200;
end loop;
dbms_output.put_line('successfully');
end;
/
2 3 4 5 6 7 8 9 10 11
Procedure created.
创建一个存储过程p2循环8000次(循环的越多执行的时间越长),在循环的过程中我们可以在v$session_wait视图上捕捉到“db file sequential read”select count(*) into leo from leo1 where object_id>=100 and object_id<=200 与 上面的sql执行过程类似只是多了一个汇总过程,也走的是索引范围扫描。
LEO1@LEO1> alter system flush buffer_cache; 同样也需要先清空data_buffer_cache,原理同上
System altered.
LEO1@LEO1> execute p2; 当看到“successfully”表明这个p2执行完毕
successfully
PL/SQL procedure successfully completed.
会话157
LEO1@LEO1> select sid,event,wait_class,p1,p1text,p2,p2text,p3,p3text from v$session_wait where event like '%sequential%';
SID EVENT WAIT_CLASS P1 P1TEXT P2 P2TEXT P3 P3TEXT
---------- ---------------------------------------------------------------- ----------------- ---------- ---------- ---------------
133 db file scattered read User I/O 5 file# 2852 block# 1 blocks
在157会话中当对133的存储过程p2执行过程中,马上查看v$session_wait视图,才能捕捉到“db file sequential read”,如果执行完毕了就看不到这个等待事件了,切记切记切记!
参数含义都是一样的,只是p3指单数据块读取,只能显示p3=1,这也符合数据块顺序读的机制
LEO1@LEO1> alter system flush buffer_cache; 再次清空data_buffer_cache
System altered.
10046 trace文件中显示出来
小提示:当你做实验次数多了时候,会发现要访问的trace文件非常的大在里面找到自己测试sql语句是一件很麻烦的事,这时我们可以选择先清空它在进行测试。
[oracle@leonarding1 trace]$ cd /u01/app/oracle/diag/rdbms/leo1/LEO1/trace/
[oracle@leonarding1 trace]$ ll | grep 6327 找到6327号文件
-rw-r----- 1 oracle oinstall 187300 Dec 29 01:49 LEO1_ora_6327.trc
-rw-r----- 1 oracle oinstall 1231 Dec 29 01:49 LEO1_ora_6327.trm
[oracle@leonarding1 trace]$ > LEO1_ora_6327.trc 清空它既可
[oracle@leonarding1 trace]$ ll | grep 6327 现在是一个空文件了
-rw-r----- 1 oracle oinstall 0 Dec 29 09:28 LEO1_ora_6327.trc
LEO1@LEO1> alter session set events '10046 trace name context forever,level 12'; 启动10046事件
Session altered.
LEO1@LEO1> select count(*) from leo1 where object_id>=100 and object_id<=200; 索引范围扫描
COUNT(*)
------------------
94
LEO1@LEO1> alter system flush buffer_cache; 清空重新抽取数据
System altered.
LEO1@LEO1>select owner,object_id,object_name,object_type,status from leo1 where object_id>=100 and object_id<=200;
LEO1@LEO1> alter system flush buffer_cache; 再清空
System altered.
LEO1@LEO1> insert into leo1 select * from leo1; 插入数据也会产生顺序读,我们一会看10046事件就知道了
71959 rows created.
LEO1@LEO1> insert into leo1 select * from leo1;
143918 rows created.
LEO1@LEO1> alter session set events '10046 trace name context off'; 关闭10046事件
Session altered.
LEO1@LEO1> select name,value from v$diag_info where name='Default Trace File'; 当前会话写入trace文件名
NAME VALUE
--------------------------------------------------------------------------------
Default Trace File /u01/app/oracle/diag/rdbms/leo1/LEO1/trace/LEO1_ora_19807.trc
LEO1@LEO1> !vim /u01/app/oracle/diag/rdbms/leo1/LEO1/trace/LEO1_ora_19807.trc 打开trace文件
====================================================================================================
由于读取的数据量较少,发生的“db file sequential read”也不是很多,你的是不是也这样呢:)
select count(*) from leo1 where object_id>=100 and object_id<=200
END OF STMT
PARSE #2:c=0,e=42,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,plh=6466795,tim=1356745819699276
EXEC #2:c=0,e=67,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,plh=6466795,tim=1356745819699412
WAIT #2: nam='SQL*Net message to client' ela= 11 driver id=1650815232 #bytes=1 p3=0 obj#=0 tim=1356745819699497
WAIT #2: nam='db file sequential read' ela= 26 file#=5 block#=2851 blocks=1 obj#=73718 tim=1356745819699622
WAIT #2: nam='db file sequential read' ela= 16 file#=5 block#=3364 blocks=1 obj#=73718 tim=1356745819699706
====================================================================================================
select owner,object_id,object_name,object_type,status from leo1 where object_id>=100 and object_id<=200
END OF STMT
PARSE #4:c=0,e=108,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,plh=1434365503,tim=1356745833882227
EXEC #4:c=0,e=63,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,plh=1434365503,tim=1356745833882367
WAIT #4: nam='SQL*Net message to client' ela= 11 driver id=1650815232 #bytes=1 p3=0 obj#=0 tim=1356745833882463
WAIT #4: nam='db file sequential read' ela= 22 file#=5 block#=2851 blocks=1 obj#=73718 tim=1356745833882638
WAIT #4: nam='db file sequential read' ela= 15 file#=5 block#=3364 blocks=1 obj#=73718 tim=1356745833882720
WAIT #4: nam='db file sequential read' ela= 18 file#=5 block#=180 blocks=1 obj#=73717 tim=1356745833883105
====================================================================================================
插入数据操作中,95%会发生大量的“db file sequential read”等待事件,偶尔也能看到“db file scattered read”
因为什么呢?我们想一想插入流程是不是
第一 先从数据文件中把数据块读到内存里在,这时会发生大量的“db file sequential read” 等待事件
第二 再把内存中数据插入到数据文件里
insert into leo1 select * from leo1
END OF STMT
PARSE #3:c=3000,e=2992,p=0,cr=5,cu=0,mis=1,r=0,dep=0,og=1,plh=2716644435,tim=1356745204775114
WAIT #3: nam='db file sequential read' ela= 34 file#=5 block#=182 blocks=1 obj#=73717 tim=1356745204776532
WAIT #3: nam='Disk file operations I/O' ela= 186 FileOperation=2 fileno=3 filetype=2 obj#=73717 tim=1356745204776928
WAIT #3: nam='db file sequential read' ela= 17 file#=5 block#=184 blocks=1 obj#=73717 tim=1356745204777246
WAIT #3: nam='db file sequential read' ela= 25 file#=5 block#=186 blocks=1 obj#=73717 tim=1356745204777889
WAIT #3: nam='db file sequential read' ela= 15 file#=5 block#=188 blocks=1 obj#=73717 tim=1356745204778474
WAIT #3: nam='db file sequential read' ela= 25 file#=5 block#=190 blocks=1 obj#=73717 tim=1356745204779100
。。。。。。。。。。。还有很多不在贴了,节约空间
====================================================================================================
第二次插入为什么,只发生了2次“db file sequential read”,是不是第一次已经把大部分数据加载到内存里啦,第二次的时候就可以直接从内存里往磁盘上插数据了吧,省略了在从磁盘读这步了。
insert into leo1 select * from leo1
END OF STMT
PARSE #4:c=0,e=110,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,plh=2716644435,tim=1356745852945838
WAIT #4: nam='db file sequential read' ela= 32 file#=5 block#=4352 blocks=1 obj#=73717 tim=1356745852961373
WAIT #4: nam='db file sequential read' ela= 37 file#=5 block#=4353 blocks=1 obj#=73717 tim=1356745852984158
四 构造一个Direct Path Read等待事件,等待事件需要在v$session_wait和10046 trace文件中显示出来,贴出整个演示过程。
Direct path read 等待事件:发生在“重新读取”的时候,因为排好序数据是一种中间状态,作为计算的中间值存在,不会放在SGA中共享,直接读取到会话私有PGA中,一般是PGA的sort area区
场景:排好序数据会优先放在PGA中,当PGA装满时,就会被写入到磁盘的temp表空间里,当发生“重新读取”的时候,磁盘上的数据不会经过SGA的内存区,而是直接读取到PGA内存区里,在这个过程中将会发生“direct path read”等待事件
LEO1@LEO1> show parameter target
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
archive_lag_target integer 0
db_flashback_retention_target integer 1440
fast_start_io_target integer 0
fast_start_mttr_target integer 0
memory_max_target big integer 652M
memory_target big integer 652M
parallel_servers_target integer 8
pga_aggregate_target big integer 0
sga_target big integer 0
LEO1@LEO1> set serveroutput on
LEO1@LEO1> select table_name,index_name from user_indexes where table_name='LEO1';
TABLE_NAME INDEX_NAME
------------------------------ ------------------------------
LEO1 IDX_LEO1
我们这次利用一下之前的表leo1,我们已经重做了2次,凡是跟着做的筒子们已经由鱼得水耶,咱们就加快点进度吧。
v$session_wait视图中显示出来
LEO1@LEO1> create or replace procedure p3
as
leo number;
begin
for i in 1..1000
loop
select count(*) into leo from leo1 order by object_name;
end loop;
dbms_output.put_line('successfully');
end;
/
2 3 4 5 6 7 8 9 10 11
Procedure created.
创建存储过程p3循环1000次,既然我们要捕捉排好序的数据来产生“direct path read”等待事件,那么我们就要对记录进行order by 操作,这样我们在检索的时候才能看到我们想要的等待事件,其实order by 哪列都可以的。
LEO1@LEO1> execute p3; 当看到“successfully”表明这个p3执行完毕
successfully
PL/SQL procedure successfully completed.
会话157
LEO1@LEO1> select sid,event,wait_class,p1,p1text,p2,p2text,p3,p3text from v$session_wait where event like '%direct%';
SID EVENT WAIT_CLASS P1 P1TEXT P2 P2TEXT P3 P3TEXT
---------- ---------------------------------------------------------------- ---------- ---------- ---------- ---------- --------------- ---------- ---------------
133 direct path read User I/O 5 file numbe 1922 first dba 62 block cnt
133 direct path read User I/O 5 file numbe 3648 first dba 64 block cnt
133 direct path read User I/O 5 file numbe 4226 first dba 62 block cnt
我们看到“direct path read”事件发生在5号数据文件的1922 3648 4226号的数据块上,分别从这三个数据块起始开始读取62 64 62 个数据块。
10046 trace文件中显示出来
LEO1@LEO1> alter session set events '10046 trace name context forever,level 12'; 启动10046事件
Session altered.
LEO1@LEO1> select count(*) from leo1 order by object_name; 数据排序
COUNT(*)
----------------
71959
LEO1@LEO1> alter session set events '10046 trace name context off'; 关闭10046事件
Session altered.
LEO1@LEO1> select name,value from v$diag_info where name='Default Trace File'; 当前会话写入trace文件名
NAME VALUE
--------------------------------------------------------------------------------
Default Trace File /u01/app/oracle/diag/rdbms/leo1/LEO1/trace/LEO1_ora_19807.trc
LEO1@LEO1> !vim /u01/app/oracle/diag/rdbms/leo1/LEO1/trace/LEO1_ora_19807.trc
====================================================================================================
select count(*) from leo1 order by object_name
END OF STMT
PARSE #6:c=1000,e=1320,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=1,plh=4191104944,tim=1356754783973531
EXEC #6:c=0,e=56,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,plh=4191104944,tim=1356754783973706
WAIT #6: nam='SQL*Net message to client' ela= 12 driver id=1650815232 #bytes=1 p3=0 obj#=7 tim=1356754783973862
WAIT #6: nam='direct path read' ela= 101 file number=5 first dba=179 block cnt=13 obj#=73717 tim=1356754783974411
WAIT #6: nam='direct path read' ela= 103 file number=5 first dba=1457 block cnt=15 obj#=73717 tim=1356754783974600
WAIT #6: nam='direct path read' ela= 103 file number=5 first dba=1473 block cnt=15 obj#=73717 tim=1356754783975005
小结:看只要经过order by排序的检索就会产生“direct path read”等待事件,此时数据是直接读取到PGA内存区的。如果你不先进行排序也可能会出现“direct path read”,但还伴随着db file sequential read和db file scattered read,如果进行排序了那么99%都是direct path read等待事件了。
本文转自 leonarding151CTO博客,原文链接:http://blog.51cto.com/leonarding/1105414,如需转载请自行联系原作者