操作系统块是操作系统读写的最小操作单元,也是操作系统文件的属性之一。当创建一个Oracle数据库时,选择一个基于操作系统块的整数倍大小作为Oracle数据库块的大小。Oracle数据库读写操作则是以Oracle块为最小单位,而非操作系统块。
数据库块也称逻辑块或Oracle块,它对应磁盘上一个或多个物理块,它的大小由初始化参数DB_BLOCK_SIZE决定,可以定义数据块为2K、4K、8K、16K、32K甚至更大,默认Oracle块大小是8K。若一旦设置了Oracle数据块的大小,则在整个数据库生命期间不能被更改。使用一个合适的Oracle块大小对于数据库的调优是非常重要的。
OS在每次执行I/O的时候是以OS的块为单位;Oracle在每次执行I/O的时候是以Oracle块为单位。Oracle块具有以下特点:
① 最小的I/O单元;
② 包含一个或多个OS块;
③ 大小由参数DB_BLOCK_SIZE决定;
④ 数据库创建时设置,数据库创建后不能更改。
在Oracle中,不论数据块中存储的是表(TABLE)、索引(INDEX)或簇表(CLUSTERTABLE),其内部结构都是类似的。Oracle块的结构如下图所示:
图 3-8 Oracle 块结构图
由上图可以看出,一个Oracle块大约由数据块头(包括标准内容和可变内容,Common And Variable Header)、表目录(Table Directory)、行目录(Row Directory)、可用空间(Free Space)和行数据(Row Data)这几部分组成。图中两个箭头表示一个数据块中的可用空间区的容量是可变的。
●数据块头(BlockHeader):主要包含有数据块地址的一些基本信息(块地址,Block Address)和段的类型(表段、索引段等)。块头自上而下进行增长。
●表目录(Table Directory):如果一个堆组织表在此数据块中储存了数据行,那么该表的信息将被记录在数据块的表目录中。多个表可以将行存储在相同的块中。
●行目录(Row Directory):此区域包含数据块中存储的数据行的信息,即每个数据行片断(Row Piece)在行数据(Row Data)中的地址。一个数据块中可能保存一个完整的数据行,也可能只保存数据行的一部分。当一个数据块(Data Block)的行目录(Row Directory)空间被使用后,即使数据行被删除(DELETE),行目录空间也不会被回收。举例来说,当一个曾经包含50条记录的数据块被清空后,其块头(Header)的行目录仍然占用100字节(Byte)的空间。仅在块中插入新行时,数据库才会重用此空间。
●可用空间(Free Space):是指可以为以后的更新和插入操作分配的空间,大小由PCTFREE和PCTUSED两个参数影响。可用空间位于块的中部,允许头和行数据空间在必要时进行增长。当插入新行或用更大的值更新现有行的列时,行数据会占用可用空间。导致块头增长的事件包括:行目录需要更多的行条目和需要的事务处理插槽数多于最初配置的数目。块中的可用空间最初是相邻的。但是,删除和更新操作可能会使块中的可用空间变成碎片。
●行数据(Row Data):数据块(Data Block)中行数据(Row Data)包含了表或索引的实际数据。一个数据行可以跨多个数据块。行数据空间自下而上进行增长。
下面介绍一下与数据块存储相关的几个概念。
1、 什么是ASSM和MSSM?
段空间有两种管理方式,分别是手动段空间管理(Manual Segment Space Management,MSSM)和自动段空间管理(Auto Segment Space Management,ASSM)。
自动段空间管理(ASSM),它首次出现在Oracle 9.2中。自由列表FREELIST被位图所取代,它是一个二进制的数组,能够迅速有效地管理存储扩展和剩余区块(Free Block),因此能够改善分段存储本质。ASSM会忽略PCTUSED参数而只使用PCTFREE参数。对于MSSM而言,可以设置FREELIST、PCTUSED和PCTFREE等参数来控制如何分配和使用段中的空间。
2、 FREELIST(自由列表)
Oracle通过维护FREELIST列表来记录或更新所有可用的数据块。当执行INSERT语句时,Oracle首先在FREELIST列表上搜索可用的空闲数据块,搜索成功之后将数据插入到那个空闲块。块在FREELIST列表中的可用性由PCTFREE参数值来决定。起初一个空块在FREELIST列表上列出,并且会一直保留,直到空闲空间达到PCTFREE设定的值。当一个块被使用且达到PCTFREE设定的值之后,该块将从FREELIST列表被移除,而当数据块的可用空间低于PCTUSED值的时候,该块又会回收,即重新回到FREELIST列表。Oracle使用FREELIST方式以提高数据库性能。因此,每一个INSERT操作,Oracle仅仅需要搜索FREELIST结构,而不是搜索所有数据块。从Oracle 9i开始,引入了ASSM,它让Oracle自动管理FREELIST。在ASSM里,Oracle使用位图方式来标记数据块是否可用,这种数据块的空间管理方式比用一个列表来管理效率更高。
3、 PCTFREE(空闲率)和PCTUSED(使用率)
PCTFREE和PCTUSED这两个参数是面试中常问到的概念。它们用来优化数据块空间的使用,控制数据块是否出现在FREELIST中。当创建或者更改任何表和索引的时候,Oracle在空间控制方面使用这两个存储参数。
●PCTFREE:块中保留用于UPDATE操作的空间百分比,当数据占用的空间达到此上限时,新的数据将不能再插入到此块中。当数据块中的FREE空间小于PCTFREE设置的空间时,该数据块从FREELIST中去掉,当块由于DML操作FREE空间大于PCTUSED设置的空间时,该数据库块将被添加在FREELIST链表中。对于表和索引来说,该值默认为10%,通过查询DBA_TABLES或DBA_INDEXES视图的PCT_FREE列可以获取到该属性的值。该值适用于MSSM和ASSM。
●PCTUSED:指定块中数据使用空间的最低百分比;用于为插入一新行数据的最小空间的百分比。这个值决定了块的可用状态。可用状态的块可以执行插入操作,不可用状态的块只能执行删除和修改,可用状态的块被放在FREELIST中。该值只针对表有效,默认值为40%,通过查询DBA_TABLES视图的PCT_USED列可以获取到该属性的值。该值仅适用于MSSM。
若要修改表的PCTFREE和PCTUSED的值,可以使用ALTER语句修改。需要注意的是,修改之后只对新块起作用,若要对表中原有的块起作用,则可以使用MOVE重新组织表,SQL语句如下所示:
ALTER TABLE T_TEST_LHR PCTFREE 20;
ALTER TABLE T_TEST_LHR MOVE;
若要修改索引的PCTFREE的值,可以使用如下的SQL语句:
ALTER INDEX PK_TEST_LHR REBUILD PCTFREE 20;
下面给出一个示例:
SYS@lhrdb> CREATE TABLE TEST_BLOCK_LHR(
2 COL1 CHAR(20),
3 COL2 NUMBER) ;
Table created.
SYS@lhrdb> CREATE INDEX IND_COL1_LHR ON TEST_BLOCK_LHR(COL1);
Index created.
SYS@lhrdb> SELECT T.PCT_FREE,
2 T.PCT_USED,
3 T.INI_TRANS
4 FROM DBA_TABLES T
5 WHERE T.TABLE_NAME ='TEST_BLOCK_LHR';
PCT_FREE PCT_USED INI_TRANS
---------- ---------- ----------
10 40 1
SYS@lhrdb> SELECT T.PCT_FREE,
2 T.ini_trans
3 FROM DBA_INDEXES T
4 WHERE T.TABLE_NAME ='TEST_BLOCK_LHR';
PCT_FREE INI_TRANS
---------- ----------
10 2
SYS@lhrdb> ALTER TABLE TEST_BLOCK_LHR PCTFREE 20 PCTUSED 60;--修改表的PTCFREE和PCTUSED的值
Table altered.
SYS@lhrdb> SELECT T.PCT_FREE,
2 T.PCT_USED,
3 T.INI_TRANS
4 FROM DBA_TABLES T
5 WHERE T.TABLE_NAME ='TEST_BLOCK_LHR';
PCT_FREE PCT_USED INI_TRANS
---------- ---------- ----------
20 60 1
SYS@lhrdb> ALTER TABLE TEST_BLOCK_LHR MOVE;--重新组织表
Table altered.
SYS@lhrdb> ALTER INDEX IND_COL1_LHR REBUILD PCTFREE 20;--修改索引的PTCFREE值
Index altered.
下面详细介绍一下数据库块的dump结构。首先准备如下的表:
CREATE TABLE T_TESTBLOCK_20160926_LHR( ID NUMBER, NAME VARCHAR(4) ) TABLESPACE TS_TESTBLOCKLHR;
INSERT INTO T_TESTBLOCK_20160926_LHR VALUES (1, 'a');
INSERT INTO T_TESTBLOCK_20160926_LHR VALUES (2, 'b');
INSERT INTO T_TESTBLOCK_20160926_LHR VALUES (3, 'c');
COMMIT;
SYS@lhrdb> SELECT ROWID,DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID) REL_FNO,DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID) BLOCKNO FROM T_TESTBLOCK_20160926_LHR;
ROWID REL_FNO BLOCKNO
------------------ ---------- ----------
AAAXh8AAHAAAACDAAA 7 131
AAAXh8AAHAAAACDAAB 7 131
AAAXh8AAHAAAACDAAC 7 131
可以看到这3行数据都在7号数据文件的第131个块。将该数据块dump出来:
SYS@lhrdb> ALTER SYSTEM DUMP DATAFILE 7 BLOCK 131;
System altered.
SYS@lhrdb> SELECT VALUE FROM V$DIAG_INFO WHERE NAME LIKE '%Default%';
VALUE
--------------------------------------------------------------------------------
/oracle/app/oracle/diag/rdbms/lhrdb/lhrdb/trace/lhrdb_ora_12386484.trc
以下是该数据块的完整dump结果:
[LHRDB1:oracle]:/oracle/app/oracle/diag/rdbms/lhrdb/lhrdb/trace>more lhrdb_ora_12386484.trc
Trace file /oracle/app/oracle/diag/rdbms/lhrdb/lhrdb/trace/lhrdb_ora_12386484.trc
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
With the Partitioning, Real Application Clusters, Automatic Storage Management, OLAP,
Data Mining and Real Application Testing options
ORACLE_HOME = /oracle/app/oracle/product/11.2.0/db
System name: AIX
Node name: LHRDB1
Release: 1
Version: 7
Machine: 00F63A614C00
Instance name: lhrdb
Redo thread mounted by this instance: 1
Oracle process number: 30
Unix process pid: 12386484, image: oracle@LHRDB1 (TNS V1-V3)
*** 2016-09-26 11:33:32.934
*** SESSION ID:(190.1277) 2016-09-26 11:33:32.934
*** CLIENT ID:() 2016-09-26 11:33:32.934
*** SERVICE NAME:(SYS$USERS) 2016-09-26 11:33:32.934
*** MODULE NAME:(sqlplus@LHRDB1 (TNS V1-V3)) 2016-09-26 11:33:32.934
*** ACTION NAME:() 2016-09-26 11:33:32.934
*** TRACE FILE RECREATED AFTER BEING REMOVED ***
Start dump data blocks tsn: 8 file#:7 minblk 131 maxblk 131
Block dump from cache:
Dump of buffer cache at level 4 for tsn=8 rdba=29360259
BH (0x700010023ff51d0) file#: 7 rdba: 0x01c00083 (7/131) class: 1 ba: 0x700010023ee4000
set: 19 pool: 3 bsz: 8192 bsi: 0 sflg: 1 pwc: 41,22
dbwrid: 0 obj: 96380 objn: 96380 tsn: 8 afn: 7 hint: f
hash: [0x7000100627ed2a0,0x7000100627ed2a0] lru: [0x700010023ff6640,0x700010023ff5a10]
ckptq: [NULL] fileq: [NULL] objq: [0x70001005dfd2cf0,0x70001005dfd2cf0] objaq: [0x70001005dfd2ce0,0x70001005dfd2ce0]
st: XCURRENT md: NULL fpin: 'ktspbwh2: ktspfmdb' tch: 2
flags: block_written_once redo_since_read
LRBA: [0x0.0.0] LSCN: [0x0.0] HSCN: [0xffff.ffffffff] HSUB: [1]
Block dump from disk:
buffer tsn: 8 rdba: 0x01c00083 (7/131)
scn: 0x0000.00752951 seq: 0x01 flg: 0x06 tail: 0x29510601
frmt: 0x02 chkval: 0x4235 type: 0x06=trans data
Hex dump of block: st=0, typ_found=1
Dump of memory from 0x0000000110ADCC00 to 0x0000000110ADEC00
110ADCC00 06A20000 01C00083 00752951 00000106 [.........u)Q....]
110ADCC10 42350000 01000007 0001787C 00752950 [B5........x|.u)P]
110ADCC20 00000000 00023200 01C00080 00080002 [......2.........]
110ADCC30 000009E9 00C0108B 04AC2400 20030000 [..........$. ...]
110ADCC40 00752951 00000000 00000000 00000000 [.u)Q............]
110ADCC50 00000000 00000000 00000000 00000000 [................]
110ADCC60 00000000 00010003 FFFF0018 1F801F65 [...............e]
110ADCC70 1F650000 00031F90 1F881F80 00000000 [.e..............]
110ADCC80 00000000 00000000 00000000 00000000 [................]
Repeat 501 times
110ADEBE0 00000000 2C010202 C1040163 2C010202 [....,......c,...]
110ADEBF0 C1030162 2C010202 C1020161 29510601 [...b,......a)Q..]
Block header dump: 0x01c00083
Object id on Block? Y
seg/obj: 0x1787c csc: 0x00.752950 itc: 2 flg: E typ: 1 - DATA
brn: 0 bdba: 0x1c00080 ver: 0x01 opc: 0
inc: 0 exflg: 0
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0008.002.000009e9 0x00c0108b.04ac.24 --U- 3 fsc 0x0000.00752951
0x02 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
bdba: 0x01c00083
data_block_dump,data header at 0x110adcc64
===============
tsiz: 0x1f98
hsiz: 0x18
pbl: 0x110adcc64
76543210
flag=--------
ntab=1
nrow=3
frre=-1
fsbo=0x18
fseo=0x1f80
avsp=0x1f65
tosp=0x1f65
0xe:pti[0] nrow=3 offs=0
0x12:pri[0] offs=0x1f90
0x14:pri[1] offs=0x1f88
0x16:pri[2] offs=0x1f80
block_row_dump:
tab 0, row 0, @0x1f90
tl: 8 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 02
col 1: [ 1] 61
tab 0, row 1, @0x1f88
tl: 8 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 03
col 1: [ 1] 62
tab 0, row 2, @0x1f80
tl: 8 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 04
col 1: [ 1] 63
end_of_block_dump
End dump data blocks tsn: 8 file#: 7 minblk 131 maxblk 131
下面对该dump内容做详细解说:
一、块头信息区
首先看头部信息区分析:
Block dump from disk:
buffer tsn: 8 rdba: 0x01c00083 (7/131)
scn: 0x0000.00752951 seq: 0x01 flg: 0x06 tail: 0x29510601
frmt: 0x02 chkval: 0x4235 type: 0x06=trans data
① buffer tsn: 8 表示该块对应的表空间号,这里是8号表空间
② rdba: 0x01c00083 (7/131) 其中,rdba(relative data block address)表示相对数据块地址,其中(7/131)表示该块为7号数据文件第131个块,用4个字节32位来表示,前10位为相对数据文件号,后22位为块号。01c00083(十六进制)=0000 0001 1100 0000 0000 0000 1000 0011(二进制),不难看出前10位(0000 0001 11)转换成十进制就是7,后22位(00 0000 0000 0000 1000 0011)转换成十进制就是131。rdba在数据块中的offset是4,即rdba存在于数据块中的第5-9字节中(offset从0开始算),数据块中的每个部分在数据块中的偏移量通过BBED可以展示出来
③ scn: 0x0000.00752951 表示数据块头部SCN,总共占用6个字节,前2个字节(0000)表示SCN Wrap,后4个字节(00752951)表示SCN Base。如果SCN Base达到了4个字节表示的最大值,SCN Wrap+1,SCN Base清0。在数据块中的offset是8。这里的SCN号为7678289
④ seq: 0x01 表示Sequence number即日志序列号。在数据块中的offset是14
⑤ flg: 0x06 flg即Flag,其中,0x01代表New block即新建块;0x02代表Delayed Logging Change advanced SCN即数据块延迟清洗推进scn和seq;0x04代表Check value即设置校验和;0x08代表Temporary block即临时块。其它值都是01、02、04、08的组合。在数据块中的offset是15
⑥ tail: 0x29510601即tail check,存放于数据块的最后4个字节,用于数据块一致性检查。tail check的组成:SCN Base的低2个字节+type+seq。即tail:0x32d30601=32d3+06+01
⑦ frmt: 0x02 代表块格式。01表示Oracle 7,02表示Oracle 8+
⑧ chkval: 0x4235 代表块检查值。如果参数DB_BLOCK_CHECKSUM=TRUE,那么数据块在读入buffer和写回数据文件之前都要做检查计算,如果计算值和数据块中记录的计算值不匹配就会标记该块是坏块
⑨ type: 0x06=trans data 代表块类型,参考以下的表格:
ID
Type
01
Undo segment header
02
Undo data block
03
Save undo header
04
Save undo data block
05
Data segment header (temp, index, data and so on)
06
KTB managed data block (with ITL)
07
Temp table data block (no ITL)
08
Sort Key
09
Sort Run
10
Segment free list block
11
Data file header
二、事务列表区
Block header dump: 0x01c00083
Object id on Block? Y
seg/obj: 0x1787c csc: 0x00.752950 itc: 2 flg: E typ: 1 - DATA
brn: 0 bdba: 0x1c00080 ver: 0x01 opc: 0
inc: 0 exflg: 0
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0008.002.000009e9 0x00c0108b.04ac.24 --U- 3 fsc 0x0000.00752951
0x02 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
①、Object id on Block? Y 表示该块是否属于某个对象
②、seg/obj: 0x1787c 表示该数据块中对象的OBJECT_ID。本示例dump的是表T_TESTBLOCK_20160926_LHR,下面来验证一下:
SYS@lhrdb> select to_number('1787c','xxxxxx') from dual;
TO_NUMBER('1787C','XXXXXX')
---------------------------
96380
SYS@lhrdb> select object_name,object_type from dba_objects where object_id=96380;
OBJECT_NAME OBJECT_TYPE
---------------------------- -------------------
T_TESTBLOCK_20160926_LHR TABLE
③、csc: 0x00.752950 表示SCN at last Block CleanOut,表示最后一次块清除(Block CleanOut)时候的SCN
④、itc: 2 块中ITL slot的数量,根据下面的ITL图可以看出的确只有2个ITL slot
⑤、flg: E 0表示此块被放置在自由列表(freelist)中,E指用的是ASSM
⑥、typ: 1 DATA 表示数据库块的类型,1表示数据,2表示索引
⑦、bdba: 0x1c00080 Block relative data block address,相对数据块的地址
接下来的内容属于ITL的范围:
图 3-9 ITL图
ITL(Interested Transaction List,事务槽)是Oracle数据块内部的一个组成部分,用来记录在该块上发生的所有事务。1个ITL可以看作是一个记录,在一个时间,可以记录一个事务(包括提交或者未提交事务)。当然,如果这个事务已经提交,那么这个ITL的位置就可以被反复使用了,因为LTL与记录非常类似,所以,有的时候也叫ITL槽位。
ITL位于数据块头(Block Header),ITL事务槽由槽位号(Itl)、Xid(Transaction ID)、Uba(Undo Block Address)、Flag、Lck和Scn/Fsc几个部分组成。Oracle的每个数据块中都有一个或者多个事务槽,每一个对数据块的并发访问事务都会占用一个事务槽。对于已经提交的事务,ITL槽位不会马上被覆盖,因为一致性读可能会用到这个信息,一致性读的时候,可能需要从这里获得回滚段的入口,并从回滚段中获得一致性读。
当发出一条SQL语句时,Oracle会记录下这个时刻的SCN,然后在Buffer Cache中查找需要的BLOCK,或者从磁盘上读。当别的会话修改了数据,或者正在修改数据时,就会在相应的块上记录ITL,此时Oracle发现ITL中记录的SCN大于SELECT时刻的SCN,那么Oracle就会根据ITL中的Uba找到Undo信息获得该BLOCK的前镜像,然后在Buffer Cache中构造出CR(consistent read)块,此时Oralce也会检查构造出来的BLOCK中ITL记录的SCN。如果SCN还大于SELECT时刻的SCN,那么一直重复构造前镜像,然后Oracle找到前镜像BLOCK中的ITL的SCN是否小于SELECT的SCN,同时检查这个事务有没有提交或者回滚。如果没有,那么继续构造前镜像,直到找到需要的BLOCK,如果在构造前镜像的过程中所需的Undo信息被覆盖了,就会报快照过旧的错误。
如果一个事务一直没有提交,那么这个事务将一直占用一个ITL槽位。如果这个事务已经提交,那么,ITL槽位中还保存的有这个事务提交时候的SCN号。
ITL的个数受参数INITRANS控制,最大ITL个数受MAXTRANS控制(Oracle 10g已废弃MAXTRANS,默认最大支持255个并发)。在一个块内部,默认分配了2个ITL的个数。如果这个块内还有空闲空间(Free Space),那么Oracle是可以利用这些空闲空间并再次分配ITL。如果没有了空闲空间,那么这个块会因为不能分配新的ITL可能发生ITL等待,即enq: TX - allocate ITL entry等待事件。
在并发量特别大的系统中,最好分配足够的ITL个数,或者设置足够的PCTFREE,保证ITL能扩展。但是,PCTFREE有可能是被行数据给消耗掉的,如UPDATE,所以,也有可能导致块内部的空间不够而导致ITL等待。对于表(数据块)来说,INITRANS这个参数的默认值是1。对于索引(索引块)来说,这个参数默认值是2。
下面详细介绍ITL的结构:
① Itl:ITL事务槽编号,ITL事务槽号的流水编号
② Xid:代表对应的事务id(transac[X]tion identified),在回滚段事务表中有一条记录和这个事务对应。Xid由3列使用十六进制编码的数字列表示,分别是:Undo Segment Number + Transaction Table Slot Number + Wrap,即由undo段号+undo槽号+undo槽号的覆盖次数三部分组成,即usn.slot.sqn,这里0x0008.002.000009e9转换为10进制为8.2.2537,从下边的查询出的结果是相对应的:
SYS@lhrdb> SELECT XIDUSN,XIDSLOT,XIDSQN,UBAFIL,UBABLK,UBASQN,UBAREC FROM V$TRANSACTION;
XIDUSN XIDSLOT XIDSQN UBAFIL UBABLK UBASQN UBAREC
---------- ---------- ---------- ------- ---------- -------- ----------
8 2 2537 3 4235 1196 36
③ Uba:即Undo Block Address,该事务对应的回滚段地址,记录了最近一次的该记录的前镜像(修改前的值)。Uba组成:Undo块地址(undo文件号和数据块号)+回滚序列号+回滚记录号。多版本一致读是Oracle保证读操作不会被事务阻塞的重要特性。当Server Process需要查询一个正在被事务修改,但是尚未提交的数据时,就根据ITL上的Uba定位到对应Undo前镜像数据位置。这里的Uba为:0x00c0108b.04ac.24,其中00c0108b(16进制)=0000 0000 1100 0000 0001 0000 1000 1011(2进制,共32位,前10位代表文件号,后22位代表数据块号)=文件号为3,块号为4235(10进制);04ac(16进制)=1196(10进制);24(16进制)=36(10进制)。Uba的值可以从V$TRANSACTION查询出来:
SELECT UBAFIL 回滚段文件号,UBABLK数据块号,UBASQN回滚序列号,UBAREC回滚记录号FROM v$transaction ; --查看UBA
④ Flag:事务标志位,即当前事务槽的状态信息。这个标志位记录了这个事务的操作状态,各个标志的含义分别是:
标识
简介
----
事务是活动的,未提交,或者在块清除前提交事务。
C---
事务已经提交,锁已经被清除(提交)。
-B--
包含ITL记录的UNDO数据。
--U-
事务已经提交,但是锁还没有清除(快速提交)。
---T
块清除的SCN被记录时,该事务仍然是活动的,块上如果有已经提交的事务,那么在clean ount的时候,块会被进行清除,但是这个块里面的事务不会被清除。
C-U-
块被延迟清除,回滚段的信息已经改写,SCN显示为最小的SCN,需要由回滚段重新生成,例如在提交以前,数据块已经刷新到数据文件上。
⑤ Lck:表示这个事务所影响的行数,锁住了几行数据,对应有几个行锁。在这里,可以看到01号事务槽Lck为3,因为该事务槽中的事务Flag为U,证明该事务已经提交,但是锁还没有清除。再比如对于下边这个ITL:
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0006.020.00000271 0x00800205.0257.13 C--- 0 scn 0x0000.001732c4
0x02 0x0008.006.00000279 0x00800351.0278.15 ---- 1 fsc 0x0000.00000000
看到01号事务槽Lck为0,因为该事务槽中的事务Flag为C,证明该事务已经提交,锁已经被清除,该事务槽可以被重用。02号事务槽Lck为1,是因为对第一行做了一个更新操作,并且没有提交,Flag为“----”说明该事务是活动的。
⑥ Scn/Fsc:Scn表示提交时的SCN。Commit SCN或者快速提交(Fast Commit,Fsc)的SCN。Scn=SCN of commited TX;Fsc=Free space credit(bytes)。每条记录中的行级锁对应Itl条目lb,对应于Itl列表中的序号,即那个事务在该记录上产生的锁。
对于一个Oracle事务来说,可以是快速提交、也可以是延迟提交,目的都是为了提高提交的速度。提交以后,Oracle需要对ITL事务槽、每一行的锁定标记进行清除。如果是快速提交,那么在提交的时候,会将事务表和每一个数据块的ITL槽进行清除。但是锁定标记可能没有清除,等下次用到的时候再进行清除。如果是延迟提交,那么在提交的时候,只是将事务表进行清除,并没有对ITL事务槽进行清除,每一行的锁定标记也没有清除。因此,C和U的情况特别多。块清除的过程并不包括每个行的锁定标记的清除,主要指的是ITL的清除。
注意:
① 事务槽中首先记录的是XID和UBA,只有在提交以后,当对这个数据块进行CLEANOUT的时候,才会更新FLAG和SCN。因此,Oracle总是以事务表中对这个数据块的SCN以及FLAG为准。
② 一个事务开始以后,在一个数据块上得到一个事务槽,那么在这个事务提交以前,这个事务槽会一直占用,直到这个事务提交才会释放这个事务槽。
③ 只有在已经提交以后,这个ITL事务槽中的SCN才会有数值。
④ 事务是否已经提交、事务对应的SCN,这些信息都是以回滚段事务表中的为主,事务槽中的不准确。
⑤ 事务槽中的事务ID和UBA地址是准确的。
⑥ 事务槽中的事务ID和回滚段中的事务ID肯定不是一样的,不同回滚段中的事务ID也一定不一样。
三、用户数据头区(data_block_dump)
行目录(Row Directory)记录了数据块里每一行相对于起点的偏移量,Oracle正是通过行目录找到所需的数据行。
bdba: 0x01c00083
data_block_dump,data header at 0x110adcc64
===============
tsiz: 0x1f98
hsiz: 0x18
pbl: 0x110adcc64
76543210
flag=--------
ntab=1
nrow=3
frre=-1
fsbo=0x18
fseo=0x1f80
avsp=0x1f65
tosp=0x1f65
0xe:pti[0] nrow=3 offs=0
0x12:pri[0] offs=0x1f90
0x14:pri[1] offs=0x1f88
0x16:pri[2] offs=0x1f80
① bdba: 0x01c00083 block dba/rdba(数据块地址),用4个字节32位来表示,前10位为相对数据文件号,后22位为块号。01c00083(十六进制)=0000 0001 1100 0000 0000 0000 1000 0011(二进制),可以看到前10位(0000 0001 11)转换成十进制就是7,后22位(00 0000 0000 0000 1000 0011)转换成十进制就是131,即7号文件131号数据块
② tsiz: 0x1f98 Total Data Area Size(数据区的大小,块的总大小),转换为10进制即8088字节
③ hsiz: 0x18 数据块头大小,转换为10进制即24字节
④ pbl: 0x110adcc64 指向这个数据块在内存中映像的指针
⑤ flag=-------- N=pcrfree hit(clusters);F=do not put on free list;K=flushable cluster keys
⑥ ntab=1 number of tables (>1 is a cluster)
⑦ nrow=3 即行数,这里表示这个表有3行数据
⑧ frre=-1 first free row index entry, -1=you have to add one(没有创建索引)
⑨ fsbo=0x18 free space begin offset(空闲空间起始位置),叫起始空间:可以存放数据空间的起始位置(即定义了数据层中空闲空间的起始offset)
⑩ fseo=0x1f80 free space end offset(空闲空间结束位置),叫结束空间:可以存放数据空间的结束位置(即定义了数据层中空闲空间的结束offset)
? avsp=0x1f65 available space in the block(可用空间),叫空闲空间:定义了数据层中空闲空间的字节数
? tosp=0x1f65 total available space when all txs commit,叫最终空闲空间:定义了ITL中事务提交后,数据层中空闲空间的字节数
? 0xe:pti[0] nrow=3 offs=0 Table directory,整个表的开始,该块有3条记录
? 0x12:pri[0] offs=0x1f5e 第1条记录在偏移量为0x1f5e的地方,下面两行以此类推
? 0x14:pri[1] offs=0x1f66
? 0x16:pri[2] offs=0x1f80
四、用户数据区(block_row_dump)
block_row_dump:
tab 0, row 0, @0x1f90
tl: 8 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 02
col 1: [ 1] 61
tab 0, row 1, @0x1f88
tl: 8 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 03
col 1: [ 1] 62
tab 0, row 2, @0x1f80
tl: 8 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 04
col 1: [ 1] 63
end_of_block_dump
① tab0, row 0, @0x1f90 第一个表第一行的位置,定义了该表在行索引中的起始插槽号
② lb: 0x1 表示lock byte。锁定该行的这个事务在ITL的入口,0x1说明事务在该数据行上的锁还没清除,并且该锁指向01号事务槽。lb: 0x0说明事务在该数据行上的锁已经被清除
③ tl: 8 表示Row Size(number of bytes plus data)
④ fb Flag Byte
K- Cluster key
H- head of row piece
D- Deleted row
F- first data piece
L- last data piece
P- First column cintinues from previous row N- Last column cintinues in next piece
当DELETE一行数据的时候,数据并不是物理地被删除,而是把该行标记为删除,这个时候fb应该是--HDFL--而不是原来的--H-FL--。
⑤ cc表示number of columns in this Row piece
关于行中的数据,可以以第一行来说明一下。由于表的第2行数据为(2,'b'),所以可以使用dump函数来验证一下。dump函数可以按指定的格式显示输入数据的内部表示,这里显示16进制:
tl: 8 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 03
col 1: [ 1] 62
SYS@lhrdb> select dump(2,16),dump('b',16)from dual;
DUMP(2,16) DUMP('B',16)
----------------- ----------------
Typ=2 Len=2: c1,3 Typ=96 Len=1: 62