数据块概览
操作系统块是操作系统读写的最小操作单元,也是操作系统文件的属性之一。当创建一个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)或簇表(CLUSTER TABLE),其内部结构都是类似的。Oracle块的结构如下图所示:

Oracle 数据库使用块开销来管理块本身。块开销不可用于存储用户数据。如图所示,块开销包括以下部分:
块头
此部分包含有关块的一般信息,包括磁盘地址和段类型。对于事务管理的块,块头包含活动和历史事务信息
每个更新块的事务都需要事务条目。Oracle 数据库最初在块头中为事务条目预留空间。在分配给支持事务性更改的段的数据块中,可用空间还可以在头空间耗尽时保存事务条目。事务项所需的空间依赖于操作系统。然而,大多数操作系统中的事务条目大约需要23字节。
表目录
对于堆组织的表,此目录包含有关其行存储在此块中的表的元数据。多个表可以在同一块中存储行。
行目录
对于堆组织的表,此目录描述块的数据部分中行的位置。
在行目录中分配了空间之后,数据库在删除行之后不会回收这个空间。因此,当前为空但以前最多有50行的块将继续为行目录分配100个字节。只有在块中插入新行时,数据库才重用此空间。
块开销的某些部分的大小是固定的,但是总的大小是可变的。平均而言,块开销总共为84到107个字节。
行格式
块的行数据部分包含实际数据,如表行或索引键条目。正如每个数据块都有一个内部格式一样,每一行都有一个行格式,使数据库能够跟踪行中的数据。
Oracle 数据库将行存储为可变长度的记录。一行包含在一个或多个行片中。每一行都有一个行标题和列数据。
行标题
Oracle 数据库使用行标题管理存储在块中的行片段。行标题包含以下信息:
行片段
位于其他数据块中的行片段
如果可以将整个行插入到单个数据块中,则 Oracle 数据库将该行作为一个行块存储。但是,如果不能将所有行数据插入到单个块中,或者更新导致现有行超出其块的大小,那么数据库将该行存储在多个行片段中(请参阅“链接行和迁移行”)。数据块通常每行只包含一个行块。
表群集的群集键
完全包含在一个块中的行具有至少3个字节的行标题。
列数据
在行标题之后,列数据部分将实际数据存储在行中。行片通常按 createtable 语句中列出的顺序存储列,但不保证这个顺序。例如,LONG 类型的列是最后创建的。
如图12-7所示,对于行中的每个列,Oracle 数据库分别存储列长度和数据。所需的空间取决于数据类型。如果列的数据类型是可变长度的,那么保存值所需的空间可以随着数据的更新而增长和缩小。
每一行在数据块标题的行目录中都有一个槽。槽指向行的开头

Data Block是数据库中最小的I/O单元,下面我来简单介绍下数据块的基本结构。
OK!跟着我一步步实验:
一、建表空间
SYS@PROD> col file_name for a45
SYS@PROD> col tablespace_name for a10
SYS@PROD> select file_id,file_name,tablespace_name,bytes/1024/1024 M,status,AUTOEXTENSIBLE from dba_data_files order by 1;
FILE_ID FILE_NAME TABLESPACE M STATUS AUTOEXT
---------- --------------------------------------------- ---------- ---------- --------- -------
1 /u01/app/oracle/oradata/PROD/system01.dbf SYSTEM 740 AVAILABLE YES
2 /u01/app/oracle/oradata/PROD/sysaux01.dbf SYSAUX 500 AVAILABLE YES
3 /u01/app/oracle/oradata/PROD/undotbs01.dbf UNDOTBS1 30 AVAILABLE YES
4 /u01/app/oracle/oradata/PROD/users01.dbf USERS 5 AVAILABLE YES
SYS@PROD> create tablespace tp1 datafile '/u01/app/oracle/oradata/PROD/tp101.dbf' size 10M;
Tablespace created.
二、建用户及授权
SYS@PROD> create user bw identified by oracle default tablespace tp1;
User created.
SYS@PROD> grant dba to bw;
Grant succeeded.
SYS@PROD> conn bw/oracle
Connected.
三、建表
BW@PROD> create table t1 (id int,name varchar2(100));
Table created.
四、插入一行数据
BW@PROD> insert into t1 values(1,'AAAAA');
1 row created.
BW@PROD> commit;
Commit complete.
五、手动发生一个检查点,使上面一行数据写到数据文件
BW@PROD> alter system checkpoint;
System altered.
六、查这行数据所在的文件号和块号
BW@PROD> col name for a10
BW@PROD> select id,name,dbms_rowid.rowid_relative_fno(rowid) file#,dbms_rowid.rowid_block_number(rowid) block# from t1;
ID NAME FILE# BLOCK#
---------- ---------- ---------- ----------
1 AAAAA 5 132
七、转储6号文件135号块,新开个窗口
SYS@PROD> alter system dump datafile 5 block 135;
System altered.
八、找到转储的文件
SYS@PROD> SELECT VALUE FROM V$DIAG_INFO WHERE NAME LIKE '%Default%';
VALUE
-------------------------------------------------------------------------------------------
/u01/app/oracle/diag/rdbms/prod/PROD/trace/PROD_ora_17033.trc
九、分析数据块结构
Trace file /u01/app/oracle/diag/rdbms/prod/PROD/trace/PROD_ora_3949.trc
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
ORACLE_HOME = /u01/app/oracle/product/11.2.0/db_1
System name: Linux
Node name: vmac1
Release: 3.8.13-16.2.1.el6uek.x86_64
Version: #1 SMP Thu Nov 7 17:01:44 PST 2013
Machine: x86_64
Instance name: PROD
Redo thread mounted by this instance: 1
Oracle process number: 19
Unix process pid: 3949, image: oracle@vmac1 (TNS V1-V3)
*** 2019-11-07 21:15:01.500
*** SESSION ID:(165.37) 2019-11-07 21:15:01.500
*** CLIENT ID:() 2019-11-07 21:15:01.500
*** SERVICE NAME:(SYS$USERS) 2019-11-07 21:15:01.500
*** MODULE NAME:(sqlplus@vmac1 (TNS V1-V3)) 2019-11-07 21:15:01.500
*** ACTION NAME:() 2019-11-07 21:15:01.500
Start dump data blocks tsn: 6 file#:5 minblk 132 maxblk 132
Block dump from cache:
Dump of buffer cache at level 4 for tsn=6 rdba=20971652
BH (0x82fe74c0) file#: 5 rdba: 0x01400084 (5/132) class: 1 ba: 0x82dc8000
set: 5 pool: 3 bsz: 8192 bsi: 0 sflg: 1 pwc: 66,28
dbwrid: 0 obj: 87358 objn: 87358 tsn: 6 afn: 5 hint: f
hash: [0x973fceb0,0x973fceb0] lru: [0x82fe76e8,0x82fe7478]
ckptq: [NULL] fileq: [NULL] objq: [0x82fe7980,0x82fe74a0] objaq: [0x82fe7990,0x82fe74b0]
st: XCURRENT md: NULL fpin: 'ktspbwh2: ktspfmdb' tch: 3
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: 6 rdba: 0x01400084 (5/132)
scn: 0x0000.000edf37 seq: 0x01 flg: 0x06 tail: 0xdf370601
frmt: 0x02 chkval: 0x67a0 type: 0x06=trans data
Hex dump of block: st=0, typ_found=1
Dump of memory from 0x00007FE7B05C2A00 to 0x00007FE7B05C4A00
7FE7B05C2A00 0000A206 01400084 000EDF37 06010000 [......@.7.......]
7FE7B05C2A10 000067A0 00000001 0001553E 000EDF34 [.g......>U..4...]
7FE7B05C2A20 00000000 00320002 01400080 00210005 [......2...@...!.]
7FE7B05C2A30 0000036B 00C00DDB 002500D0 00002001 [k.........%.. ..]
7FE7B05C2A40 000EDF37 00000000 00000000 00000000 [7...............]
7FE7B05C2A50 00000000 00000000 00000000 00000000 [................]
7FE7B05C2A60 00000000 00010100 0014FFFF 1F781F8C [..............x.]
7FE7B05C2A70 00001F78 1F8C0001 00000000 00000000 [x...............]
7FE7B05C2A80 00000000 00000000 00000000 00000000 [................]
Repeat 502 times
7FE7B05C49F0 0202012C 410502C1 41414141 DF370601 [,......AAAAA..7.]
Block header dump: 0x01400084
Object id on Block? Y
seg/obj: 0x1553e csc: 0x00.edf34 itc: 2 flg: E typ: 1 - DATA
brn: 0 bdba: 0x1400080 ver: 0x01 opc: 0
inc: 0 exflg: 0
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0005.021.0000036b 0x00c00ddb.00d0.25 --U- 1 fsc 0x0000.000edf37
0x02 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
bdba: 0x01400084
data_block_dump,data header at 0x7fe7b05c2a64
===============
tsiz: 0x1f98
hsiz: 0x14
pbl: 0x7fe7b05c2a64
76543210
flag=--------
ntab=1
nrow=1
frre=-1
fsbo=0x14
fseo=0x1f8c
avsp=0x1f78
tosp=0x1f78
0xe:pti[0] nrow=1 offs=0
0x12:pri[0] offs=0x1f8c
block_row_dump:
tab 0, row 0, @0x1f8c
tl: 12 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 02
col 1: [ 5] 41 41 41 41 41
end_of_block_dump
End dump data blocks tsn: 6 file#: 5 minblk 132 maxblk 132
1、数据块头部分
Block dump from disk:
buffer tsn: 6 rdba: 0x01400084 (5/132)
scn: 0x0000.000edf37 seq: 0x01 flg: 0x06 tail: 0xdf370601
frmt: 0x02 chkval: 0x67a0 type: 0x06=trans data
Hex dump of block: st=0, typ_found=1
Dump of memory from 0x00007FE7B05C2A00 to 0x00007FE7B05C4A00
7FE7B05C2A00 0000A206 01400084 000EDF37 06010000 [......@.7.......]
7FE7B05C2A10 000067A0 00000001 0001553E 000EDF34 [.g......>U..4...]
7FE7B05C2A20 00000000 00320002 01400080 00210005 [......2...@...!.]
7FE7B05C2A30 0000036B 00C00DDB 002500D0 00002001 [k.........%.. ..]
7FE7B05C2A40 000EDF37 00000000 00000000 00000000 [7...............]
7FE7B05C2A50 00000000 00000000 00000000 00000000 [................]
7FE7B05C2A60 00000000 00010100 0014FFFF 1F781F8C [..............x.]
7FE7B05C2A70 00001F78 1F8C0001 00000000 00000000 [x...............]
7FE7B05C2A80 00000000 00000000 00000000 00000000 [................]
Repeat 502 times
7FE7B05C49F0 0202012C 410502C1 41414141 DF370601 [,......AAAAA..7.]
buffer tsn: 6 rdba: 0x01400087(5/132):
表示该块对应的表空间号,这里是5号表空间
0x01400087 (5/132) 其中,rdba(relative data block address)表示相对数据块地址,
其中(5/132))表示该块为5号数据文件第132个块,用4个字节32位来表示,前10位为相对数据文件号,后22位为块号。
scn: 0x0000.000edf37 seq: 0x01 flg: 0x06 tail: 0xdf370601
frmt: 0x02 chkval: 0x67a0 type: 0x06=trans data
scn: 0x0000.000ec392 表示数据块头部SCN,总共占用6个字节,前2个字节(0000)表示SCN Wrap,后4个字节(000ec392)表示SCN Base。
如果SCN Base达到了4个字节表示的最大值,SCN Wrap+1,SCN Base清0。在数据块中的offset是8。这里的SCN号为967570
seq: 0x01 表示Sequence number即日志序列号。在数据块中的offset是14。修改一次seq增长。(测试update 01>02)
flg: 0x06 flg即Flag,其中,
0x01代表New block即新建块;
0x02代表Delayed Logging Change advanced SCN即 数据块延迟清洗推进scn和seq;
0x04代表Check value即设置校验和;
0x08代表Temporary block即临时块。
tail: 0xdf370601 即tail check,存放于数据块的最后4个字节,用于数据块一致性检查。
tail check的组成:SCN Base的低2个字节+type+seq。即tail:0xdf370601=32d3+06+01
数据块的最后四字节 tail: 0xdf370601 = scnBASE+flg+seq, 如果不相等会报块损坏!!!
frmt: 0x02 代表块格式。01表示Oracle 7,02表示Oracle 8+
(8i~9i 都是0x02 10.1.0 2k: 0x62 4k:0x82 8k:0xa2 16k:0xc2 (logfile 0x22 512 bytes))
chkval: 0x67a0 代表块检查值。如果参数DB_BLOCK_CHECKSUM=TRUE,那么数据块在读入buffer和写回数据文件之前都要做检查计算,
如果计算值和数据块中记录的计算值不匹配就会标记该块是坏块 (后期可以研究下数据数据块校验事怎么工作的)
type: 0x06=trans data 代表块类型,参考以下的表格:

2.ITL 事务槽
Block header dump: 0x01400084
Object id on Block? Y
seg/obj: 0x1553e csc: 0x00.edf34 itc: 2 flg: E typ: 1 - DATA
brn: 0 bdba: 0x1400080 ver: 0x01 opc: 0
inc: 0 exflg: 0
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0005.021.0000036b 0x00c00ddb.00d0.25 --U- 1 fsc 0x0000.000edf37
0x02 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
bdba: 0x01400084
data_block_dump,data header at 0x7fe7b05c2a64
ITL(Interested Transaction List,事务槽)是Oracle数据块内部的一个组成部分,用来记录在该块上发生的所有事务。
1个ITL可以看作是一个记录,在一个时间,可以记录一个事务(包括提交或者未提交事务)。当然,如果这个事务已经提交,那么这个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控制(DDL 建表语句),最大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。
SYS@PROD> select dbms_metadata.get_ddl('TABLE','T1','BW') from dual;
DBMS_METADATA.GET_DDL('TABLE','T1','BW')
--------------------------------------------------------------------------------
CREATE TABLE "BW"."T1"
( "ID" NUMBER(*,0),
"NAME" VARCHAR2(100)
) SEGMENT CREATION IMMEDIATE
PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255
NOCOMPRESS LOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
TABLESPACE "TP1"
好那我们进一步分析,dump文件:
Block header dump: 0x01400084
Object id on Block? Y
seg/obj: 0x1553e csc: 0x00.edf34 itc: 2 flg: E typ: 1 - DATA
brn: 0 bdba: 0x1400080 ver: 0x01 opc: 0
inc: 0 exflg: 0
seg/obj: 0x1553e --16进制转成10进制87358
SYS@PROD> select object_id from dba_objects where object_name='T1' and owner='BW';
OBJECT_ID
----------
87358
csc: 0x00.edf34 --cleanoutSCN,块清除时的SCN
itc: 2 --ITL槽的数量
flg: E flg: E --指用的是ASSM,如果是O表示用的是free list
typ: 1 - DATA --事务型的数据块(并且:数据块头的type:0x06),存放表和索引数据。
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0005.021.0000036b 0x00c00ddb.00d0.25 --U- 1 fsc 0x0000.000edf37
0x02 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
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,这里0x0005.021.0000036b转换为10进制为5.33.875,从下边的查询出的结果是相对应的:
SELECT XIDUSN,XIDSLOT,XIDSQN,UBAFIL,UBABLK,UBASQN,UBAREC FROM V$TRANSACTION;
XIDUSN XIDSLOT XIDSQN | UBAFIL UBABLK UBASQN UBAREC
---------- ---------- ---------- | ------- ---------- -------- ----------
5 33 875 | 3 4235 1196 36
Uba:(下面有实验验证)
即Undo Block Address,该事务对应的回滚段地址,记录了最近一次的该记录的前镜像(修改前的值)。
Uba组成:Undo块地址(undo文件号和数据块号)+回滚序列号+回滚记录号。多版本一致读是Oracle保证读操作不会被事务阻塞的重要特性。
当Server Process需要查询一个正在被事务修改,但是尚未提交的数据时,就根据ITL上的Uba定位到对应Undo前镜像数据位置。
这里的Uba为:0x00c00ddb.00d0.25,其中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进制)。
SELECT UBAFIL 回滚段文件号,UBABLK 数据块号,UBASQN 回滚序列号,UBAREC 回滚记录号 FROM v$transaction ; --查看UBA
Flag:(下面有实验验证)
事务标志位,即当前事务槽的状态信息。这个标志位记录了这个事务的操作状态,各个标志的含义分别是:
(Flg C=Committed U=Commit Upper Bound T=Active at CSC)
---- 事务是活动的,未提交,或者在块清除前提交事务。
C— 事务已经提交,锁已经被清除(提交)。
-B-- 包含ITL记录的UNDO数据。
–U- 事务已经提交,但是锁还没有清除(快速提交)。
—T 块清除的SCN被记录时,该事务仍然是活动的,块上如果有已经提交的事务,那么在clean ount的时候,块会被进行清除,但是这个块里面的事务不会被清除。
C-U- 块被延迟清除,回滚段的信息已经改写,SCN显示为最小的SCN,需要由回滚段重新生成,例如在提交以前,数据块已经刷新到数据文件上。
Scn/Fsc:(没有提交 空闲预支字节数(FSC),提交scn)
这里fsc 0x0000.000edf37是指提交的scn,这个值大于上次清除块时的scn=csc: 0x00.edf34 (此scn是这个块中最小的SCN of commited)
SCN WRAP:如果事务已提交并完成清洗,该字段保存事务提交SCN的SCN WRAP部分,否则该字段保存空闲预支字节数(FSC).
比如我删除了一行数据10个字节,在事务提前前,这10个字节就属于fsc(即会写到SCN WRAP),只有事务提交后,才能正式返回到空闲空间。
每条记录中的行级锁对应Itl条目lb,对应于Itl列表中的序号,即那个事务在该记录上产生的锁。
对于一个Oracle事务来说,可以是快速提交、也可以是延迟提交,目的都是为了提高提交的速度。
提交以后,Oracle需要对ITL事务槽、每一行的锁定标记进行清除。
因此,C和U的情况特别多。块清除的过程并不包括每个行的锁定标记的清除,主要指的是ITL的清除。
3.数据头信息(表目录+行目录)
bdba: 0x01400084
data_block_dump,data header at 0x7fe7b05c2a64
===============
tsiz: 0x1f98
hsiz: 0x14
pbl: 0x7fe7b05c2a64
76543210
flag=--------
ntab=1
nrow=1
frre=-1
fsbo=0x14
fseo=0x1f8c
avsp=0x1f78
tosp=0x1f78
0xe:pti[0] nrow=1 offs=0
0x12:pri[0] offs=0x1f8c
bdba:
0x01400084 block dba/rdba(数据块地址),用4个字节32位来表示,前10位为相对数据文件号,后22位为块号。
tsiz:
0x1f98 Total Data Area Size(数据区的大小,块的总大小),转换为10进制即8088字节
hsiz:
0x18 数据块头大小,转换为10进制即24字节 --Data header size 数据头大小即20个字节
pbl:
0x7fe7b05c2a64 指向这个数据块在内存中映像的指针
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=1
即行数,这里表示这个表有3行数据
frre=-1
first free row index entry, -1=you have to add one(没有创建索引)
fsbo=0x14
free space begin offset(空闲空间起始位置),叫起始空间:可以存放数据空间的起始位置(即定义了数据层中空闲空间的起始offset)
fseo=0x1f8c
free space end offset(空闲空间结束位置),叫结束空间:可以存放数据空间的结束位置(即定义了数据层中空闲空间的结束offset)
avsp=0x1f78
available space in the block(可用空间),叫空闲空间:定义了数据层中空闲空间的字节数
tosp=0x1f78
total available space when all txs commit,叫最终空闲空间:定义了ITL中事务提交后,数据层中空闲空间的字节数
0xe:pti[0]
nrow=1 offs=0 Table directory,整个表的开始,该块有1条记录 --Table directory,整个表的开始,共一行数据 ,定义了该表在行索引中使用的插槽数
0x12:pri[0] offs=0x1f8c
第1条记录在偏移量为0x1f5e的地方,下面两行以此类推 --Row index,叫行索引,定义了该块中包含的所有行数据的位置
4.行数据
block_row_dump:
tab 0, row 0, @0x1f8c
tl: 12 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 02
col 1: [ 5] 41 41 41 41 41
tab 0, row 0, @0x1f90
第一个表第一行的位置,定义了该表在行索引中的起始插槽号
lb:
0x1 表示lock byte。锁定该行的这个事务在ITL的入口,0x1说明事务在该数据行上的锁还没清除,并且该锁指向01号事务槽。
lb: 0x0说明事务在该数据行上的锁已经被清除
0x1 --Lock byte和上面的ITL的lck相对应,表示这行是否被lock了
tl: 12 表示Row Size(number of bytes plus data) -行头,tl: 12行长度12个字节,
fb: --H-FL–
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: 2
表示number of columns in this Row piece --表示有两列,即这个表有两个字段
col 0: [ 2] c1 02 --第一行的第一个字段长度和值
col 1: [ 5] 41 41 41 41 41 --第一行的第二个字段长度和值
5、下面对这些数据用SQL语句做相互转换
(1)把表中的数据转成16进制(即在ORACLE内部数据块看到的数据),用以下sql语句:
BW@PROD> select id,name,dump(01,'16'), dump('AAAAA','16') from t1;
ID NAME DUMP(01,'16') DUMP('AAAAA','16')
---------- ---------- ----------------- ----------------------------
1 AAAAA Typ=2 Len=2: c1,2 Typ=96 Len=5: 41,41,41,41,41
(2)反过来把16进制转成表中的数据(当然我这边的数据类型只考虑了number和varchar类型),用以sql下语句:
BW@PROD> col id for 999
BW@PROD> col id1 for 999
BW@PROD> col name for a10
BW@PROD> col name1 for a10
BW@PROD> select id,UTL_RAW.CAST_TO_NUMBER(replace(' c1 02 ',' ')) id1,
2 name,UTL_RAW.CAST_TO_VARCHAR2(replace('41 41 41 41 41',' ')) name1
3 from t1;
ID ID1 NAME NAME1
---- ---- ---------- ----------
1 1 AAAAA AAAAA
十、详细研究flg标志:
flg:
0x01代表New block即新建块;
0x02代表Delayed Logging Change advanced SCN即 数据块延迟清洗推进scn和seq;
0x04代表Check value即设置校验和;
0x08代表Temporary block即临时块。
对于 flg标志的值是由上面一些位做组合运算的,下面我们来看几下例子:
1、先来看看0x01 (新建块)和0X04 (设置校验和) 的组合
SYS@PROD> select * from v$version;
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
PL/SQL Release 11.2.0.4.0 - Production
CORE 11.2.0.4.0 Production
TNS for Linux: Version 11.2.0.4.0 - Production
NLSRTL Version 11.2.0.4.0 - Production
SYS@PROD> show show parameter deferred_segment_creation
showmode OFF
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
deferred_segment_creation boolean TRUE
默认为段延迟创建,所以SEGMENT CREATION IMMEDIATE 直接分配extents:
BW@PROD> create table t20(id int ,name varchar2(10)) SEGMENT CREATION IMMEDIATE;
Table created.
BW@PROD> select extent_id,file_id,block_id from dba_extents where segment_name='T20';
EXTENT_ID FILE_ID BLOCK_ID
---------- ---------- ----------
0 5 136
SYS@PROD> alter system dump datafile 5 block 136;
System altered.
SYS@PROD> SELECT VALUE FROM V$DIAG_INFO WHERE NAME LIKE '%Default%';
VALUE
-----
/u01/app/oracle/diag/rdbms/prod/PROD/trace/PROD_ora_15577.trc
数据块头部分:
scn: 0x0000.00000000 seq: 0x01 flg: 0x05 tail: 0x00000001
frmt: 0x02 chkval: 0xa788 type: 0x00=unknown
0x01说明这是一个新建的块,因为我的表的是刚刚创建的,没有向块中插入数据。
0x04说明有设置校验,chkval: 0xa798这个就是校验和,这个与参数db_block_checksum有关
SYS@PROD> show parameter db_block_checksum
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
db_block_checksum string TYPICAL
默认设置了db_block_checksum,就会启动校验和检查数据块的一致性。另外数据块尾部的4个字节也是配合数据块头来验证数据块的一致性。
如果一个块头被标识成软损坏,那么块头的序列号为0xff,标志为0x00
2、再来看0x2 (数据块延迟清洗推进scn和seq)
在11g版本中,对数据DML操作,Oracle都是采取快速提交,在事务槽中可以观察到,OK,我先来做个测试来验证一下:
先插入一条数,然后提交,再dump,操作如下:
BW@PROD> insert into t1 values(2,'BBBBB');
1 row created.
BW@PROD> commit;
Commit complete.
BW@PROD> alter system flush buffer_cache;
System altered.
BW@PROD> select id,name,dbms_rowid.rowid_relative_fno(rowid),dbms_rowid.rowid_block_number(rowid) from t1 where id=2;
ID NAME DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID) DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)
---- ---------- ------------------------------------ ------------------------------------
2 BBBBB 3 132
sys@PROD> alter system dump datafile 3 block 132;
System altered.
数据块头
Block dump from disk:
buffer tsn: 6 rdba: 0x01400084 (5/132)
scn: 0x0000.000efb72 seq: 0x02 flg: 0x06 tail: 0xfb720602
frmt: 0x02 chkval: 0xe8e9 type: 0x06=trans data
事务ITL
Block header dump: 0x01400084
Object id on Block? Y
seg/obj: 0x1553e csc: 0x00.edf34 itc: 2 flg: E typ: 1 - DATA
brn: 0 bdba: 0x1400080 ver: 0x01 opc: 0
inc: 0 exflg: 0
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0005.021.0000036b 0x00c00ddb.00d0.25 --U- 1 fsc 0x0000.000edf37
0x02 0x0006.01f.00000427 0x00c00e2f.0083.02 --U- 1 fsc 0x0000.000efb72
数据头信息
bdba: 0x01400084
data_block_dump,data header at 0x7f145149ea64
===============
tsiz: 0x1f98
hsiz: 0x16
pbl: 0x7f145149ea64
76543210
flag=--------
ntab=1
nrow=2
frre=-1
fsbo=0x16
fseo=0x1f80
avsp=0x1f6a
tosp=0x1f6a
0xe:pti[0] nrow=2 offs=0
0x12:pri[0] offs=0x1f8c
0x14:pri[1] offs=0x1f80
行数据
block_row_dump:
tab 0, row 0, @0x1f8c
tl: 12 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 02
col 1: [ 5] 41 41 41 41 41
tab 0, row 1, @0x1f80
tl: 12 fb: --H-FL-- lb: 0x2 cc: 2
col 0: [ 2] c1 03
col 1: [ 5] 42 42 42 42 42
end_of_block_dump
End dump data blocks tsn: 6 file#: 5 minblk 132 maxblk 132
实际上很容易看出,从上面的数据看出
tab 0, row 2, @0x1f75
tl: 12 fb: --H-FL-- lb: 0x1 cc: 2 ---lb: 0x1 对应的就是事务槽的1号槽。
col 0: [ 2] c1 03
col 1: [ 5] 42 42 42 42 42 ----16进制42就是B
tab 0, row 1, @0x1f80
tl: 12 fb: --H-FL-- lb: 0x2 cc: 2 ---lb: 0x1 对应的就是事务槽的1号槽。
col 0: [ 2] c1 03
col 1: [ 5] 42 42 42 42 42 ----16进制42就是B
那么什么时候会把提交标记U变成C呢,当我再修改另一行记录时,
那下面我按上面所说再来做个操作,即插入一下数据,提交,dump,再观察 块头的 flg,事务槽,数据,及 csc。
BW@PROD> alter system checkpoint;
System altered.
BW@PROD> update t1 set name='CCCCC' where id=1;
1 row updated.
BW@PROD> commit;
Commit complete.
BW@PROD> alter system flush buffer_cache;
System altered.
sys@PROD> alter system dump datafile 3 block 132;
System altered.
dump 的内容如下:
Block dump from disk:
buffer tsn: 6 rdba: 0x01400084 (5/132)
scn: 0x0000.000f21cd seq: 0x02 flg: 0x06 tail: 0x21cd0602
frmt: 0x02 chkval: 0x4351 type: 0x06=trans data
Hex dump of block: st=0, typ_found=1
Dump of memory from 0x00007F6032B76A00 to 0x00007F6032B78A00
Block header dump: 0x01400084
Object id on Block? Y
seg/obj: 0x1553e csc: 0x00.f21c7 itc: 2 flg: E typ: 1 - DATA 991687
brn: 0 bdba: 0x1400080 ver: 0x01 opc: 0
inc: 0 exflg: 0
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0003.011.0000037e 0x00c00608.009d.29 --U- 1 fsc 0x0000.000f21cd 991693
0x02 0x0006.01f.00000427 0x00c00e2f.0083.02 C--- 0 scn 0x0000.000efb72 981874
bdba: 0x01400084
data_block_dump,data header at 0x7f6032b76a64
===============
tab 0, row 0, @0x1f8c
tl: 12 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 02
col 1: [ 5] 43 43 43 43 43
tab 0, row 1, @0x1f80
tl: 12 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 03
col 1: [ 5] 42 42 42 42 42
end_of_block_dump
End dump data blocks tsn: 6 file#: 5 minblk 132 maxblk 132
flg: 0x06 =0x02+0x04
update 后 CSC : 0x00.edf34(974644) -> 0x00.f21c7(991687)
果然是这样,当事务槽上的提交标志都是快速提交(U),那再有事务进来,Oracle先找个ITL SLOT插入事务,
顺便把其它ITL slot上的快速提交U 变成正常提交C,并且清除行锁,
我想说的就是 csc: 0x00.f21c7 数据 块延迟清洗真的推进了(原来是csc: 0x00.edf34)。
记住 csc: 0x00.f21c7 是数据本块中最小的COMMIT SCN,
实际上它表示上次事务槽上没清除的锁现在清除一下,然后就做了一个延迟清洗推进 .
当然也会进行 又可以能延迟清洗推进. 因为我本次实验 Flag是从 ---- 》 Flag --U- ITL 事务槽没有被占用。
ITL的个数受参数INITRANS控制,最大ITL个数受MAXTRANS控制(Oracle 10g已废弃MAXTRANS,默认最大支持255个并发)。
在一个块内部,默认分配了2个ITL的个数。如果这个块内还有空闲空间(Free Space),那么Oracle是可以利用这些空闲空间并再次分配ITL。
如果没有了空闲空间,那么这个块会因为不能分配新的ITL可能发生ITL等待,即enq: TX - allocate ITL entry等待事件。
十一、事务:
那什么是事务?事务的定义是一个独立的逻辑工作单元:它由特定的一系列必须作为一个整体一起成功或失败的SQL语句组成。
事务可以由多个数据操作语言(data manipulation language,DML)语句组成,但只能含有一个数据定义语言(data definition language,DDL)语句。
事务的ACID特征
A)、原子性(Atomicity)
事务中的所有动作要么都发生,要么都不发生
B)、一致性(Consistency)
事务将数据库从一种状态转变为下一种一致状态
C)、隔离性(Isolation)
一个事务的影响在该事务提交前对其他事务都不可见
D)、持久性(Durability)
事务一旦提交,其结果就是永久性的
事务的定义和特性就说到这里,我继续dump,分本一下数据块中的ITL槽,OK,我现在马上开始一个事务:
我现在把T1表中的id=3的这行的name=DDDDDD 改成EEEEEE,做UPDATE操作,不提交,让事务一直活动着。
BW@PROD> select id,name,dbms_rowid.rowid_relative_fno(rowid) file#,dbms_rowid.rowid_block_number(rowid) block# from t1 where id=3;
ID NAME FILE# BLOCK#
---- ---------- ---------- ----------
3 DDDDDD 5 132
BW@PROD> update t1 set name ='EEEEEE' where id=3;
1 row updated.
这时先不要提交,让事务活动着,一会去观察块中的ITL槽,为了能让上面修改的数据马上写到数据文件,执行缓存刷新操作,
如下:
BW@PROD> alter system flush buffer_cache;
System altered.
好,这时,我马上开一个新窗口做dump操作:
SYS@PROD> SYS@PROD> alter system dump datafile 5 block 132;
System altered.
SYS@PROD> SELECT VALUE FROM V$DIAG_INFO WHERE NAME LIKE '%Default%';
VALUE
-----
/u01/app/oracle/diag/rdbms/prod/PROD/trace/PROD_ora_29439.trc
贴出DUMP的主要内容ITL部分:
Object id on Block? Y
seg/obj: 0x1553e csc: 0x00.f26dc itc: 2 flg: E typ: 1 - DATA
brn: 0 bdba: 0x1400080 ver: 0x01 opc: 0
inc: 0 exflg: 0
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0007.011.000002a7 0x00c0058e.007d.38 ---- 1 fsc 0x0000.00000000
0x02 0x0006.002.00000438 0x00c0047f.0091.1d C--- 0 scn 0x0000.000f26af
各位兄弟注意了, OBJECT_ID与DATA_OBJECT_ID,什么时候不一样呢,一般做truncate操作,DATA_OBJECT_ID就会发生变化,这里就不细说了
好,我们的重点就是看ITL槽,从上面的ITL看出,我们刚刚操作的update正在活动的事务就是在第二个事务槽上即:
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0007.011.000002a7 0x00c0058e.007d.38 ---- 1 fsc 0x0000.00000000
我主要来分析一下Xid和Uba
首先Xid是由XIDUSN(Undo segment number)、XIDSLOT(Slot number)+XIDSQN(Sequence number)三部分组成的。
OK,即然说到事务,我们不得不看视图:v$transaction:
BW@PROD> select xid,xidusn,xidslot,xidsqn,ubafil,ubablk,ubasqn,ubarec,status from v$transaction;
XID XIDUSN XIDSLOT XIDSQN UBAFIL UBABLK UBASQN UBAREC STATUS
---------------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------------
07001100A7020000 7 17 679 3 1422 125 56 ACTIVE
从上面的视图v$transaction得到:
段落引用 XIDUSN=7 (3号回滚段)
XIDSLOT=17 (在3号回滚段的事务表的第17行),兄弟这里不要晕哦,这里的事务表是指17号回滚段的段头块。
XIDSQN=679 (事务表第17行被覆盖了679次)
把上面数据块中ITL事务槽中的Xid=0x0007.011.000002a7进行分解,你们看:
0x0007 (16进制)--> (10进制)8
011 (16进制)-->(10进制) 17 (这里有个小插曲,011 手动算以为是11十进制,却忘了已经进了一个16,进制转换马虎了)
000002a7(16进制)-->(10进制)679
分解出来完本与我们在transaction中看到的 XIDUSN XIDSLOT XIDSQN完成一样!
好,我们再来看ILT中的Uba=0x00c0058e.007d.38进行分析:
Uba由文件号、块号、序列号及记录号四部分组成的:
0000000011 0000000000010110001110
0x00c0058e(16进制)--> (10进制)由四个字节组成把它转成32位的二进制,取前面10位二进制得到0000000011=3,剩下的22位=1422
007d (16进制)--> (10进制) 125
38 (16进制)--> (10进制) 56
分解出来与我们在transaction中看到的UBAFIL UBABLK UBASQN UBAREC完成一致!
讲到这里,其实还有个跟事务分不开的东东,那就是锁,一个很重要的视图:v$lock;
BW@PROD> select userenv('sid') from dual;
USERENV('SID')
--------------
41
BW@PROD>
BW@PROD> select * from v$lock where sid=41;
ADDR KADDR SID TY ID1 ID2 LMODE REQUEST CTIME BLOCK
---------------- ---------------- ---------- -- ------ ---------- ---------- ---------- ---------- ----------
000000009758F4D8 000000009758F530 41 AE 100 0 4 0 13666 0
00007F4C3214D270 00007F4C3214D2D0 41 TM 87358 0 3 0 610 0
0000000095B73D78 0000000095B73DF0 41 TX 458769 679 6 0 610 0
这信息很有用:
TM锁:TM-87358-0
TX锁:TX-458769-679
最后总结:
数据块的组成部分,包括以下四部分:
1、数据块头
2、事务槽ITL
3、数据头信息(表目录+行目录)
4、块中数据
拓展阅读:
本文部分引用总结论坛内容:
明明白白使用数据块 ----数据块格式深入解析
部分截取自互联网,如有侵权请与我联系。
wechat: 704012932
email: pkweibu@163.com
CSDN: https://blog.csdn.net/weixin_37423880




