Oracle内核技术揭密_吕海波 学习笔记
创建一个区大小为1M的表空间TEM_TBS1,在它里面建表ma.t64,插入一笔资料后可以看到为此表分配的第一个区的区间是从第128号块开始的共128个数据块,段头(L3块)为131号块。通过dump数据块的信息可以知道131号块为L3块,130号块为L2块,128、129号块为L1块(dump步骤查看通过dump数据块信息,追溯三层结构位图信息),每个L1块记录64个数据块信息。测试插入的第一笔数据放在190号数据块,并且是块中的第一行数据(row_id为0)。换一个会话进程,插入第二笔数据,oracle会根据PID计算出一个新的HASH值来确定存放该数据的块号,通过代码可以看到第二笔数据放在251号块中。再在第一个会话进程中插入第三笔数据,由于PID没变,所以第三笔数据仍然放在了190号数据块中,并且是第二行(row_id为1)。后面共连接插入10笔数据后,发现数据基本是均匀分布在128-255这128个块中(当然可以先去掉128-131这4个L块,它们不存放插入的数据行)。
SQL> CREATE TABLESPACE TEM_TBS1 DATAFILE '/data1/oradata/ora1/tem_tps101.dbf' SIZE 100M uniform size 1M;
SQL> create table ma.t64(id number(5),name varchar2(10)) tablespace TEM_TBS1;
SQL> insert into ma.t64 values(1,'a1');
SQL> commit;
SQL> select extent_id,file_id,block_id,blocks from dba_extents where owner='MA' and segment_name='T64' order by extent_id;
EXTENT_ID FILE_ID BLOCK_ID BLOCKS
---------- ---------- ---------- ----------
0 7 128 128
SQL> select owner,segment_name,header_file,header_block from dba_segments where owner='MA' and segment_name='T64';
OWNER SEGMENT_NAME HEADER_FILE HEADER_BLOCK
------ ------------ ----------- ------------
MA T64 7 131
SQL> select DBMS_ROWID.ROWID_RELATIVE_FNO(rowid) file_id,DBMS_ROWID.ROWID_BLOCK_NUMBER(rowid) block_id,DBMS_ROWID.ROWID_ROW_NUMBER(rowid) row_id,id,name from ma.t64;
FILE_ID BLOCK_ID ROW_ID ID NAME
---------- ---------- ---------- ---------- ----------
7 190 0 1 a1
SQL> insert into ma.t64 values(2,'b2');
SQL> commit;
SQL> select DBMS_ROWID.ROWID_RELATIVE_FNO(rowid) file_id,DBMS_ROWID.ROWID_BLOCK_NUMBER(rowid) block_id,DBMS_ROWID.ROWID_ROW_NUMBER(rowid) row_id,id,name from ma.t64;
FILE_ID BLOCK_ID ROW_ID ID NAME
---------- ---------- ---------- ---------- ----------
7 190 0 1 a1
7 251 0 2 b2
SQL> insert into ma.t64 values(3,'c3');
SQL> commit;
SQL> select DBMS_ROWID.ROWID_RELATIVE_FNO(rowid) file_id,DBMS_ROWID.ROWID_BLOCK_NUMBER(rowid) block_id,DBMS_ROWID.ROWID_ROW_NUMBER(rowid) row_id,id,name from ma.t64;
FILE_ID BLOCK_ID ROW_ID ID NAME
---------- ---------- ---------- ---------- ----------
7 190 0 1 a1
7 190 1 3 c3
7 251 0 2 b2
SQL> select DBMS_ROWID.ROWID_RELATIVE_FNO(rowid) file_id,DBMS_ROWID.ROWID_BLOCK_NUMBER(rowid) block_id,DBMS_ROWID.ROWID_ROW_NUMBER(rowid) row_id,id,name from ma.t64;
FILE_ID BLOCK_ID ROW_ID ID NAME
---------- ---------- ---------- ---------- ----------
7 132 0 4 d4
7 134 0 8 h8
7 136 0 10 j10
7 184 0 5 e5
7 190 0 1 a1
7 190 1 3 c3
7 249 0 6 f6
7 251 0 2 b2
7 253 0 7 g7
7 255 0 9 i9
dump 131号块的信息,得到高水位线信息:
Extent Control Header
-----------------------------------------------------------------
Extent Header:: spare1: 0 spare2: 0 #extents: 1 #blocks: 128
last map 0x00000000 #maps: 0 offset: 2716
Highwater:: 0x01c00100 ext#: 0 blk#: 128 ext size: 128
高水位线的数据块地址为0x01c00100,转化为二进制后,左边10位是文件号,右边22位是数据块号。
Highwater:: 0x01c00100
二进制:0000 0001 1100 0000 0000 0001 0000 0000
前10位:(文件号) 0000 0001 11 = 7
后22位: (高水位线块号) 00 0000 0000 0001 0000 0000 = 256
因此高水位线为7号文件中的256号块,也呼应了之前在表ma.t64中插入的10条数据是在128-255号块中均匀分布的。两个L1块各记录64个数据块,刚好可以随机使用这128个数据块。但当如果高水位线停在192号块,这时相对路径插入只会使用前64个数据块,也就是只会使用128号这个L1块中记录的数据块,而不会使用129号这个L1块,这种情况与ASSM的随机分配数据块相冲突,在L2中找L1时不能做到完全随机。理论上此时的并发插入性能减半。之所以高水位线会停在192块,是有资料介绍:
“在区中插入第一行数据时,高水位线移到区的第一个L1块中最大的数据块后,L1记录的是128-191号块,此时的高水位线也就是在192号块;当块大小为8K,区大小为1M,这时L1中有64个数据块,高水位线就会以64个块为单位依次往后移动。刚开始ASSM的并发受高水位线的限制,当有100个进程插入资料,此时最多只能处理64个进程,这就会导致buffer busy waits。”
但个人在12C版本下测试的结果有所不同,块大小为8K,区大小为1M,高水位线刚开始就在256号块处,随着数据的增加,下一次发现高水位线在384号块处,也就是往后移动了128个块,是一个区的大小,也是2个L1块的大小。
随着空间使用的增多,L1的中记录的数据块也会增加。如下代码测试手动分配90M空间,有91个分区,然后dump出8号文件的11520号块(L1块)的信息,可以看到这个L1块中记录了256个数据块信息。
SQL> drop tablespace tbs2 including contents;
SQL> CREATE TABLESPACE TBS2 DATAFILE '/data1/oradata/ora1/tps201.dbf' SIZE 500M uniform size 1M;
SQL> create table ma.t65(id number(5),name varchar2(10)) tablespace tbs2;
SQL> alter table ma.t65 allocate extent (size 90m);
SQL> set pagesize 1000
SQL> alter session set "_sga_clear_dump"=true;
SQL> alter system dump datafile 8 block 11520;
$ cd $ORACLE_BASE/diag/rdbms/$ORACLE_SID/$ORACLE_SID/trace
*** 2019-04-17 08:52:46.743
04/17/2019 08:52:29 04/17/2019 08:52:29
Start dump data blocks tsn: 9 file#:8 minblk 11520 maxblk 11520
Block dump from cache:
Dump of buffer cache at level 4 for pdb=0 tsn=9 rdba=33565952
Block dump from disk:
Decrypting encrypted buffer before dump.
buffer tsn: 9 rdba: 0x02002d00 (8/11520)
scn: 0x0.44fdb5 seq: 0x04 flg: 0x04 tail: 0xfdb52004
frmt: 0x02 chkval: 0xc1b3 type: 0x20=FIRST LEVEL BITMAP BLOCK
Hex dump of block: st=0, typ_found=1
Dump of memory from 0x00007F8C7D1A0E00 to 0x00007F8C7D1A2E00
7F8C7D1A0E00 0000A220 02002D00 0044FDB5 04040000 [ ....-....D.....]
......
7F8C7D1A2DF0 00000000 00000000 00000000 FDB52004 [............. ..]
Dump of First Level Bitmap Block
--------------------------------
nbits : 4 nranges: 2 parent dba: 0x02000082 poffset: 139
unformatted: 255 total: 256 first useful block: 1
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 : 1
Bitmap block lock opcode 3
Locker xid: : 0x000a.01e.00000556
Dealloc scn: 3.0
Flag: 0x00000000 (-/-/-/-/-/-)
Inc #: 0 Objd: 93813
--------------------------------------------------------
DBA Ranges :
--------------------------------------------------------
0x02002d00 Length: 128 Offset: 0
0x02002d80 Length: 128 Offset: 128
0:Metadata 1:unformatted 2:unformatted 3:unformatted
4:unformatted 5:unformatted 6:unformatted 7:unformatted
......
248:unformatted 249:unformatted 250:unformatted 251:unformatted
252:unformatted 253:unformatted 254:unformatted 255:unformatted
也就是说对于每天一个分区的表,一天开始时,它里面的L1是相对较小的,也就是支持的并发量受L1中记录数据块多少限制的,随着数据量增加,区的使用增多,L1中的数据块信息也会增加,并发量也会增加。如果每天某一个比较早的时间段出现buffer busy waits这种事件,随着数据量增加,这个等待事件没有了,可以考虑是前期L1中数据块信息较少,高水位线低,影响了并发量。可以通过中间表,采用直接路径方式向表中插入数据,这样消耗undo少,再delete,这样来提升高水位线,再分区交换到正式表,这样可以解决并发问题。
此时我们再导出8号文件131号块,也就是L3块的信息,来看看数据库进行全表扫描时,是利用了L3块中的哪些有效信息。
SQL> alter system dump datafile 8 block 131;
*** 2019-04-17 09:23:02.771
Start dump data blocks tsn: 9 file#:8 minblk 131 maxblk 131
Block dump from cache:
Dump of buffer cache at level 4 for pdb=0 tsn=9 rdba=33554563
Block dump from disk:
Decrypting encrypted buffer before dump.
buffer tsn: 9 rdba: 0x02000083 (8/131)
scn: 0x0.44fdb9 seq: 0x01 flg: 0x04 tail: 0xfdb92301
frmt: 0x02 chkval: 0xc5be type: 0x23=PAGETABLE SEGMENT HEADER
Hex dump of block: st=0, typ_found=1
Dump of memory from 0x00007F8C7D1A0E00 to 0x00007F8C7D1A2E00
7F8C7D1A0E00 0000A223 02000083 0044FDB9 04010000 [#.........D.....]
......
7F8C7D1A2DF0 00000000 00000000 00000000 FDB92301 [.............#..]
Extent Control Header
-----------------------------------------------------------------
Extent Header:: spare1: 0 spare2: 0 #extents: 91 #blocks: 11648
last map 0x00000000 #maps: 0 offset: 2716
Highwater:: 0x02000084 ext#: 0 blk#: 4 ext size: 128
#blocks in seg. hdr's freelists: 0
#blocks below: 0
mapblk 0x00000000 offset: 0
Unlocked
--------------------------------------------------------
Low HighWater Mark :
Highwater:: 0x02000084 ext#: 0 blk#: 4 ext size: 128
#blocks in seg. hdr's freelists: 0
#blocks below: 0
mapblk 0x00000000 offset: 0
Level 1 BMB for High HWM block: 0x02000080
Level 1 BMB for Low HWM block: 0x02000080
--------------------------------------------------------
Segment Type: 1 nl2: 1 blksz: 8192 fbsz: 0
L2 Array start offset: 0x00001434
First Level 3 BMB: 0x00000000
L2 Hint for inserts: 0x02000082
Last Level 1 BMB: 0x02002d00
Last Level II BMB: 0x02000082
Last Level III BMB: 0x00000000
Map Header:: next 0x00000000 #extents: 91 obj#: 93813 flag: 0x10000000
Inc # 0
Extent Map
-----------------------------------------------------------------
0x02000080 length: 128
0x02000100 length: 128
......
0x02002d00 length: 128
0x02002d80 length: 128
Auxillary Map
--------------------------------------------------------
Extent 0 : L1 dba: 0x02000080 Data dba: 0x02000084
Extent 1 : L1 dba: 0x02000100 Data dba: 0x02000102
Extent 2 : L1 dba: 0x02000180 Data dba: 0x02000182
Extent 3 : L1 dba: 0x02000200 Data dba: 0x02000202
......
Extent 87 : L1 dba: 0x02002c00 Data dba: 0x02002c01
Extent 88 : L1 dba: 0x02002c00 Data dba: 0x02002c80
Extent 89 : L1 dba: 0x02002d00 Data dba: 0x02002d01
Extent 90 : L1 dba: 0x02002d00 Data dba: 0x02002d80
--------------------------------------------------------
Second Level Bitmap block DBAs
--------------------------------------------------------
DBA 1: 0x02000082
Extent Map记录了每个区的起始块位置,以及区的长度。这样就可以找到每一个区,以及区里的每一个块。如0x02000080,把这个十六进制转化为2进制,前十位是文件号,后面的就是块号,0x02000080代表的就是8号文件的128号块,也就是这个区的第一个块,通常第一个块是L1块。但随着L1中记录的数据块信息增加,如前面说的记录了256个块,那么一个L1可以记录两个区的信息(一个区是128个块),可以从辅助地图Auxillary Map中信息看出,Extent 87和Extent 88号区的L1地址都是0x02002c00。
辅助地图Auxillary Map中的Extent 0 : L1 dba: 0x02000080 Data dba: 0x02000084这一行信息,是说在0号区中第一个L1块地址是128号块,L1中的数据块起始位置为132号块。
全表扫描就是通过段头读取区地图信息,再通过区地图信息读取数据信息。