顾名思义,Oracle 进行direct path read和direct path write操作时,将会绕过buffer_cache,直接将数据读至PGA中。Oracle官方文档也似乎这么说。
但事实果真是这样吗?
接下来分2部分测试,首先测试direct path read,我们知道Oracle并行读时将会产生进行direct path read
SQL> alter system flush buffer_cache;
System altered.
SQL> select /*+ parallel(a 10)*/ * from zhoultest a;
835328 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 1476512502
--------------------------------------------------------------------------------
-------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time
| TQ |IN-OUT| PQ Distrib |
--------------------------------------------------------------------------------
-------------------------------
| 0 | SELECT STATEMENT | | 832K| 62M| 405 (2)| 00:00:05
| | | |
| 1 | PX COORDINATOR | | | | |
| | | |
| 2 | PX SEND QC (RANDOM)| :TQ10000 | 832K| 62M| 405 (2)| 00:00:05
| Q1,00 | P->S | QC (RAND) |
| 3 | PX BLOCK ITERATOR | | 832K| 62M| 405 (2)| 00:00:05
| Q1,00 | PCWC | |
| 4 | TABLE ACCESS FULL| ZHOULTEST | 832K| 62M| 405 (2)| 00:00:05
| Q1,00 | PCWP | |
--------------------------------------------------------------------------------
-------------------------------
可以看到buffer_cache中只有segment head
SQL> select a.FILE#,a.BLOCK#,b.EXTENT_ID,b.BLOCK_ID,b.blocks,a.DIRTY from v$bh a, dba_extents b where a.file#=b.file_id and a.block# between b.block_id and b.block_id+b.blocks-1
and owner='ZHOUL' and SEGMENT_NAME='ZHOULTEST' and a.status<>'free';
2
FILE# BLOCK# EXTENT_ID BLOCK_ID BLOCKS D
---------- ---------- ---------- ---------- ---------- -
7 5131 0 5129 8 N
SQL> select header_file,header_block from dba_segments where owner='ZHOUL' and SEGMENT_NAME='ZHOULTEST';
HEADER_FILE HEADER_BLOCK
----------- ------------
7 5131
进一步dump segment head
SQL> alter system dump datafile 7 block 5131;
System altered.
可以看到segment head中有HighWater Mark和Extent Map,而Oracle全表扫描时,正是通过Extent Map定位数据块,扫描至高水位以下位置。需要注意的是当表格extent很多时,
不足以放在同一个block中时,就会出现多个区位图块(小表的区位图块和segment head是同一个数据块,如本例),这时就需要一个指针指向下一个区位图块。
用Map Header:: next 0x00000000可以看出,本例中下一个区位图块为空,即只有一个区位图块。
Extent Control Header
-----------------------------------------------------------------
Extent Header:: spare1: 0 spare2: 0 #extents: 118 #blocks: 32896
last map 0x00000000 #maps: 0 offset: 2716
Highwater:: 0x01c0e609 ext#: 117 blk#: 1024 ext size: 1024
#blocks in seg. hdr's freelists: 0
#blocks below: 32896
mapblk 0x00000000 offset: 117
Unlocked
--------------------------------------------------------
Low HighWater Mark :
Highwater:: 0x01c0e609 ext#: 117 blk#: 1024 ext size: 1024
#blocks in seg. hdr's freelists: 0
#blocks below: 32896
mapblk 0x00000000 offset: 117
Level 1 BMB for High HWM block: 0x01c0e20c
Level 1 BMB for Low HWM block: 0x01c0e20c
--------------------------------------------------------
Segment Type: 1 nl2: 1 blksz: 8192 fbsz: 0
L2 Array start offset: 0x00001434
First Level 3 BMB: 0x00000000
L2 Hint for inserts: 0x01c0140a
Last Level 1 BMB: 0x01c0e20c
Last Level II BMB: 0x01c0140a
Last Level III BMB: 0x00000000
Map Header:: next 0x00000000 #extents: 118 obj#: 72659 flag: 0x10000000
Inc # 0
Extent Map
-----------------------------------------------------------------
0x01c01409 length: 8
0x01c01411 length: 8
0x01c01419 length: 8
0x01c01421 length: 8
。。。
通过以上测试我们可以推断出,Oracle在进行并行表格扫描时,需要将表格头和区位图块载入buffer_cache中,通过读取表格头的高水位标记位
和区位图块的区信息,从而定位表格数据块在数据文件中的位置,进而读取数据。
在测试中我们还同时发现了另外两个现象:
(1)Oracel扫描dba_extents(最终对x$ktfbue表格扫描)时,其实就是去读取区位图块的区信息,通过以下测试可以说明这一点。
通过bbed物理修改位图块信息
BBED> modify 0x0a
Warning: contents of previous BIFILE will be lost. Proceed? (Y/N) y
File: /oradata/mcstar/zhoul01.dbf (0)
Block: 5131 Offsets: 284 to 795 Dba:0x00000000
------------------------------------------------------------------------
0a000000 1114c001 08000000 1914c001 08000000 2114c001 08000000 2914c001
再次查询dba_extents视图,可以观察到数据发生了变化
SQL> select EXTENT_ID,FILE_ID,BLOCK_ID,BLOCKS from dba_extents where SEGMENT_NAME='ZHOULTEST' and OWNER='ZHOUL' order by 1;
EXTENT_ID FILE_ID BLOCK_ID BLOCKS
---------- ---------- ---------- ----------
0 7 5129 10
(2)Oracle在并行扫描时,将会进行表格级别的checkpoint。
Oracle使用direct path read进行并行查询,即直接从数据文件读取数据至PGA,如果有数据存在于buffer_cache时,势必会影响数据的一致性。所以在在并行扫描时,将会进行表格级别的checkpoint。
以下测试证明了这一点。
首先改变一些数据块
SQL> update zhoultest set name=lower(name) where rownum<10000;
9999 rows updated.
SQL> commit;
Commit complete.
SQL> select count(*) from v$bh a, dba_extents b where a.file#=b.file_id and a.block# between b.block_id and b.block_id+b.blocks-1
2 and owner='ZHOUL' and SEGMENT_NAME='ZHOULTEST' and a.status<>'free' and a.dirty='Y';
COUNT(*)
----------
116
发起一个并行查询
select /*+ parallel(a 10)*/ * from zhoultest a where rownum<0;
再次查询,可以看到dirty数据块已经从buffer_cache中刷往数据文件中。
SQL> /
COUNT(*)
----------
0
进一步dump buffer_cache中,我们注意到了objq,正是通过这个objq,Oracle加快了在buffer_cache中搜索对象的速度,同理ckptq的存在加快了增量checkpoint的内存搜索效率,
fileq的存在加快了表空间checkpoint的内存搜索效率。
SQL> ALTER SESSION SET EVENTS 'immediate trace name buffers level 2';
Session altered.
BH (0x213f5030) file#: 7 rdba: 0x01c0390c (7/14604) class: 1 ba: 0x2128e000
set: 6 blksize: 8192 bsi: 0 set-flg: 0 pwbcnt: 0
dbwrid: 0 obj: 76226 objn: 76226 tsn: 8 afn: 7
hash: [20fedda0,2a693b68] lru: [22fefd88,213f23b8]
obj-flags: object_ckpt_list
ckptq: [22fed090,2a6e3260] fileq: [2a6e32ec,2a6e32ec] objq: [2603ca30,2603ca30]
st: XCURRENT md: NULL tch: 1
flags: buffer_dirty gotten_in_current_mode redo_since_read
LRBA: [0x9331.ede6.0] HSCN: [0xa00.e7f7af9] HSUB: [1]
buffer tsn: 8 rdba: 0x01c0390c (7/14604)
scn: 0x0a00.0e7f7af9 seq: 0x02 flg: 0x02 tail: 0x7af90602
frmt: 0x02 chkval: 0x0000 type: 0x06=trans data
需要注意的是,在Oracle 9i中,并没有objq存在
BH (0x0x53fc77ac) file#: 9 rdba: 0x02410873 (9/67699) class 1 ba: 0x0x5360a000
set: 6 dbwrid: 0 obj: 44558 objn: 44558
hash: [83519fa8,83519fa8] lru: [53fc767c,53fc796c]
LRU flags: moved_to_tail
ckptq: [NULL] fileq: [NULL]
st: XCURRENT md: NULL rsop: 0x(nil) tch: 0
flags: only_sequential_access
LRBA: [0x0.0.0] HSCN: [0xffff.ffffffff] HSUB: [255] RRBA: [0x0.0.0]
buffer tsn: 9 rdba: 0x02410873 (9/67699)
scn: 0x0a00.06200be8 seq: 0x01 flg: 0x04 tail: 0x0be80601
frmt: 0x02 chkval: 0x4d10 type: 0x06=trans data
我们知道drop table时,也会进行表格级别ckpt,当drop table时,需要在buffer_cache中搜寻和这张表格相关的数据块,如果在高并发Oracle9i环境下,极易引起latch争用,
如lacth:cache buffer chain等。
我们知道在insert时,加hint:/*+ append*/将会执行direct path write,即Oracle绕过buffer_cache,直接写往数据文件。是不是Oracle所有的
数据块都绕过buffer_cache都写往数据文件呢?
接下来继续测试direct path write。
为避免干扰,我们从sys.t1读取内容插往zhoultest表格
SQL> explain plan for insert /*+ append*/ into zhoultest select * from sys.t1;
Explained.
SQL> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 1069440229
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | INSERT STATEMENT | | 51050 | 63M| 143 (4)| 00:00:02 |
| 1 | LOAD AS SELECT | ZHOULTEST | | | | |
| 2 | TABLE ACCESS FULL| T1 | 51050 | 63M| 143 (4)| 00:00:02 |
--------------------------------------------------------------------------------
9 rows selected.
SQL> alter system flush buffer_cache;
System altered.
SQL> insert /*+ append*/ into zhoultest select * from sys.t1;
420896 rows created.
SQL> commit;
Commit complete.
我们看到在buffer_cache中除了segment head还出现了其他数据块。
SQL> select a.FILE#,a.BLOCK#,b.EXTENT_ID,b.BLOCK_ID,b.blocks,a.DIRTY from v$bh a, dba_extents b where a.file#=b.file_id and a.block# between b.block_id and b.block_id+b.blocks-1
and owner='ZHOUL' and SEGMENT_NAME='ZHOULTEST' and a.status<>'free';
2
FILE# BLOCK# EXTENT_ID BLOCK_ID BLOCKS D
---------- ---------- ---------- ---------- ---------- -
7 5131 0 5129 8 Y
7 5130 0 5129 8 Y
7 24844 101 24841 1024 Y
7 29962 102 29961 1024 Y
7 29964 102 29961 1024 Y
7 29961 102 29961 1024 Y
7 29963 102 29961 1024 Y
7 30986 103 30985 1024 Y
7 30988 103 30985 1024 Y
7 30985 103 30985 1024 Y
7 30987 103 30985 1024 Y
FILE# BLOCK# EXTENT_ID BLOCK_ID BLOCKS D
---------- ---------- ---------- ---------- ---------- -
7 32010 104 32009 1024 Y
7 32012 104 32009 1024 Y
7 32009 104 32009 1024 Y
7 32011 104 32009 1024 Y
7 33036 105 33033 1024 Y
7 33033 105 33033 1024 Y
7 33035 105 33033 1024 Y
7 33034 105 33033 1024 Y
7 34057 106 34057 1024 Y
7 34059 106 34057 1024 Y
7 34058 106 34057 1024 Y
FILE# BLOCK# EXTENT_ID BLOCK_ID BLOCKS D
---------- ---------- ---------- ---------- ---------- -
7 34060 106 34057 1024 Y
23 rows selected.
进一步dump数据块,可以看出是2级位图块
Dump of Second Level Bitmap Block
number: 230 nfree: 21 ffree: 138 pdba: 0x01c0140b
Inc #: 0 Objd: 72659
opcode:0
xid:
L1 Ranges :
--------------------------------------------------------
0x01c01409 Free: 1 Inst: 1
0x01c01419 Free: 1 Inst: 1
0x01c01429 Free: 1 Inst: 1
0x01c01439 Free: 1 Inst: 1
0x01c01449 Free: 1 Inst: 1
。。。
继续dump其他数据块,看出是1级位图块
SQL> alter system dump datafile 7 block 34060;
System altered.
Dump of First Level Bitmap Block
--------------------------------
nbits : 4 nranges: 1 parent dba: 0x01c0140a poffset: 185
unformatted: 97 total: 256 first useful block: 0
owning instance : 1
instance ownership changed at
Last successful Search
Freeness Status: nf1 0 nf2 0 nf3 0 nf4 0
Extent Map Block Offset: 4294967295
First free datablock : 159
Bitmap block lock opcode 0
Locker xid: : 0x0000.000.00000000
Inc #: 0 Objd: 72659
HWM Flag: HWM Set
Highwater:: 0x01c088a8 ext#: 106 blk#: 927 ext size: 1024
#blocks in seg. hdr's freelists: 0
#blocks below: 21535
mapblk 0x00000000 offset: 106
。。。
通过以上测试我们可以得出以上推论,在进行direct path write时,由于要改变数据块内容,Oracle需要将反映数据块位置状态的L3,L2,L1,SEGMENT_HEAD读至buffer_cache中。
需要注意的是当进行direct path write时,Oracle会将segment的low hwm至high hwm未格式化的block进行格式化操作,即low hwm=high hwm,然后在high hwm之后插数据
直接路径插入之前
Extent Control Header
-----------------------------------------------------------------
Extent Header:: spare1: 0 spare2: 0 #extents: 119 #blocks: 33920
last map 0x00000000 #maps: 0 offset: 2716
Highwater:: 0x01c0f309 ext#: 118 blk#: 1024 ext size: 1024
#blocks in seg. hdr's freelists: 0
#blocks below: 33916
mapblk 0x00000000 offset: 118
Unlocked
--------------------------------------------------------
Low HighWater Mark :
Highwater:: 0x01c0ef19 ext#: 118 blk#: 16 ext size: 1024
#blocks in seg. hdr's freelists: 0
#blocks below: 32908
mapblk 0x00000000 offset: 118
Level 1 BMB for High HWM block: 0x01c0ef0c
Level 1 BMB for Low HWM block: 0x01c0ef09
仅直接路径插入一行数据
SQL> insert /*+ append*/ into zhoultest select * from sys.t1 where rownum<2;
1 row created.
SQL> commit;
Commit complete.
再次查看segment head,可以看到high Highwater mask= Low HighWater Mark
Extent Control Header
-----------------------------------------------------------------
Extent Header:: spare1: 0 spare2: 0 #extents: 120 #blocks: 34944
last map 0x00000000 #maps: 0 offset: 2716
Highwater:: 0x01c0f40e ext#: 119 blk#: 5 ext size: 1024
#blocks in seg. hdr's freelists: 0
#blocks below: 33925
mapblk 0x00000000 offset: 119
Disk Lock:: Locked by xid: 0x002a.021.00000822
--------------------------------------------------------
Low HighWater Mark :
Highwater:: 0x01c0f40e ext#: 119 blk#: 5 ext size: 1024
#blocks in seg. hdr's freelists: 0
#blocks below: 33925
mapblk 0x00000000 offset: 119
Level 1 BMB for High HWM block: 0x01c0f409
Level 1 BMB for Low HWM block: 0x01c0f409 |