暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

大话 Oracle Block(一) - 概念,实验

1697

数据块概览

操作系统块是操作系统读写的最小操作单元,也是操作系统文件的属性之一。当创建一个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块的结构如下图所示:

image.png

Oracle 数据库使用块开销来管理块本身。块开销不可用于存储用户数据。如图所示,块开销包括以下部分:

块头

此部分包含有关块的一般信息,包括磁盘地址和段类型。对于事务管理的块,块头包含活动和历史事务信息

每个更新块的事务都需要事务条目。Oracle 数据库最初在块头中为事务条目预留空间。在分配给支持事务性更改的段的数据块中,可用空间还可以在头空间耗尽时保存事务条目。事务项所需的空间依赖于操作系统。然而,大多数操作系统中的事务条目大约需要23字节。


表目录

对于堆组织的表,此目录包含有关其行存储在此块中的表的元数据。多个表可以在同一块中存储行。


行目录

对于堆组织的表,此目录描述块的数据部分中行的位置。

在行目录中分配了空间之后,数据库在删除行之后不会回收这个空间。因此,当前为空但以前最多有50行的块将继续为行目录分配100个字节。只有在块中插入新行时,数据库才重用此空间。

块开销的某些部分的大小是固定的,但是总的大小是可变的。平均而言,块开销总共为84到107个字节。

行格式

块的行数据部分包含实际数据,如表行或索引键条目。正如每个数据块都有一个内部格式一样,每一行都有一个行格式,使数据库能够跟踪行中的数据。

Oracle 数据库将行存储为可变长度的记录。一行包含在一个或多个行片中。每一行都有一个行标题和列数据。

行标题

Oracle 数据库使用行标题管理存储在块中的行片段。行标题包含以下信息:

行片段


位于其他数据块中的行片段


如果可以将整个行插入到单个数据块中,则 Oracle 数据库将该行作为一个行块存储。但是,如果不能将所有行数据插入到单个块中,或者更新导致现有行超出其块的大小,那么数据库将该行存储在多个行片段中(请参阅“链接行和迁移行”)。数据块通常每行只包含一个行块。


表群集的群集键

完全包含在一个块中的行具有至少3个字节的行标题。

列数据

在行标题之后,列数据部分将实际数据存储在行中。行片通常按 createtable 语句中列出的顺序存储列,但不保证这个顺序。例如,LONG 类型的列是最后创建的。

如图12-7所示,对于行中的每个列,Oracle 数据库分别存储列长度和数据。所需的空间取决于数据类型。如果列的数据类型是可变长度的,那么保存值所需的空间可以随着数据的更新而增长和缩小。

每一行在数据块标题的行目录中都有一个槽。槽指向行的开头

image.png

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 代表块类型,参考以下的表格:

image.png

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、块中数据

拓展阅读:

Oracle DML锁 机制探究

本文部分引用总结论坛内容:
明明白白使用数据块 ----数据块格式深入解析

                          部分截取自互联网,如有侵权请与我联系。
                          wechat: 704012932
                          email:  pkweibu@163.com
                          CSDN:   https://blog.csdn.net/weixin_37423880
最后修改时间:2021-05-14 16:56:48
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论