Data Block是如何存储数据的以及行链接Row Chaining

 

 


基础知识

l  磁盘上每个sector大小为512byte

l  逻辑 Tablespace-->Segment(最大1024不连续的Extent)-->Extent(连续的Block)-->Block(DB_BLOCK_SIZE指定2|4|8|16|32|64kb)

l  Oracle为数据库中的所有数据分配逻辑结构空间.数据库空间的单位是数据块block,范围extent,segment.

Oracle数据块blockOracle使用和分配的最小存储单位.它是由数据库建立时设置的DB_BLOCK_SIZE决定的.一旦数据库生成了,数据块的大小不能改变.要想改变只能重新建立数据不过可以建立新的不同块大小的表空间。修改方式为:
ALTER system set db_16k_cache_size=10m;
create tablespace tb_ca2 datafile 'd:\oracle\dbfile\tb_ca2.dbf' size 2m blocksize 16k;

l  Block内容header,row dictionary,free space,data从头和尾分别向中间写直到满.记录从一个Block到另外一个Block 增加1IO开销。关于Block深入分析请参考10g Data block Structure(Dump+BBED)

l  行链接和行迁移
行链接 Row Chaining:当Insert的时候,如果一个Block不能存放一条记录,其他的记录会存储到同个Extent中的其他Block
行迁移 Row Migration:当Update的时候导致行长增加了,存储的Block已经满了,这是就会发生行迁移。Oracle会迁移整行数据到一个能够存储下整行数据的Block中,迁移的原始指针指向新的存放行数据的Block,所以ROWID不变。

l  Split Block数据块分裂的问题

因为alter tablespace *** begin backup;这时只是冻结了SCN,但是却不能阻止对数据的修改,也就是说CP过程中DBWR仍然会往数据块中写入数据,备份完后就会出现数据文件不一致状态.oracle为了防止这种事情发生会把联机时的记录记录到REDO,这就是为什么热备时redo会很大


 

1 搭建环境查看ExtentBlock信息


首先建表,插入数据,随后激活CheckPoint来确保数据写入数据块,之后从dbms_rowid 中看出table t 的数据在datafile 5 的第16527 block

create table t (col1 varchar2(4000), col2 varchar2(4000), col3 varchar2(4000)) tablespace ETMCDB;

insert into t values (lpad('1', 4000, '1'), lpad('2', 4000, '2'), lpad('3', 4000, '3'));

alter system checkpoint;

exec DBMS_STATS.GATHER_TABLE_STATS(ownname=>USER, tabname=>'T');

找到L1 Bitmap Block

select owner,segment_name,tablespace_name,extent_id,file_id,block_id from dba_extents where owner='SYS' and segment_name='T';

OWNER SEGMENT_NAME TABLESPACE_NAME EXTENT_ID FILE_ID BLOCK_ID

----- ------------ --------------- --------- ------- --------

SYS              T          ETMCDB         0       7      105

找到Header Block

select owner,segment_name,tablespace_name,header_file,header_block from dba_segments where owner='SYS' and segment_name='T';

OWNER SEGMENT_NAME TABLESPACE_NAME HEADER_FILE HEADER_BLOCK

----- ------------ --------------- ----------- ------------

SYS              T          ETMCDB           7          107

实际上数据只占了5Block

SELECT num_rows, blocks, round(num_rows/blocks) AS rows_per_block FROM user_tables WHERE table_name = 'T';

  NUM_ROWS     BLOCKS ROWS_PER_BLOCK

---------- ---------- --------------

         1          5              0

根据Rowid找到真正写入数据的Block

select rowid,col1 from t;

COL1  ROWID

---- -------------------- ------------------

0001 AAAGF2AAIAAAAnbAAA

set serveroutput on

exec show_rowid('AAAM89AAHAAAABwAAA');

rowid_type..............................1

object_number...........................53053

relative_fno............................7

block_number............................112

row_number..............................0

或者

select dbms_rowid.rowid_relative_fno('AAAM89AAHAAAABwAAA') file_num,

       dbms_rowid.rowid_block_number('AAAM89AAHAAAABwAAA') block_num from dual;

  FILE_NUM  BLOCK_NUM

---------- ----------

         7        112

然后就Dump header

alter system dump datafile 7 block 105;  

找到Extent内每个块具体的作用(L1 L2 Bitmap, Header, Data)根据上边的Dump header结果


(L1 dba:  0x01800049 Data dba:  0x0180004c)

select dbms_utility.DATA_BLOCK_ADDRESS_FILE(to_number('1800049','xxxxxxxxxx')) file#,

       dbms_utility.DATA_BLOCK_ADDRESS_BLOCK(to_number('1800049','xxxxxxxxxx')) block#

  from dual;

查看进制存储格式(未测试)

--还可以用下面的语句然后再用上面的dump 语句看内存中block 的进制存储格式

--但这内存中的进制格式和datafileblock数据表示的顺序会略有不同可能是内存寻址的缘故吧

ALTER SESSION SET EVENTS '10289 trace name context forever, level 1';

ALTER SESSION SET EVENTS '10289 trace name context off';

2 分析Dump Block Trc 文件格式 具体内容参考我这篇《10g Data block Structure(Dump+BBED)》这里就是再做一次总结
 


·    Header (Common and Variable) 包含该块的总信息例如块地址还有该段的类型数据或是索引

·    Table Directory 包含该块中数据所在的表的信息。

·    Row Directory 包含该块中的行的信息例如每个 row piece 的地址。数据块头的 row directory 空间一但分配后当行被删除delete对应的 row directory 中的空间不会被同时收回。因此,就可能出现一个数据块是空的,但在块头的 row directory 中,记录了50行信息,占用100 bytes。仅当有新的行插入该数据块时,Oracle 才会重用该空间。

·    Overhead header, table directory, row directory 共同组成 overhead。数据块的 overhead 有些是固定大小的总的 overhead 大小是可变的。平均来说,数据块头的 overhead 部分的 固定大小部分 可变大小部分 占用 84 bytes 107 bytes

·    Data(Row Data) 包含表或索引的实际数据。行可以跨块存储。

·    Free Space 用来存储插入的新行或已有行的更新需要更多的空间来存储数据。数据块的空闲空间大小和参数 PCTFREE 共同决定该块中是否可以再插入数据。In data blocks allocated for the data segment of a table or cluster, or for the index segment of an index, free space can also hold transaction entries. A transaction entry is required in a block for each INSERT, UPDATE, DELETE, and SELECT...FOR UPDATE statement accessing one or more rows in the block. The space required for transaction entries is operating system dependent; however, transaction entries in most operating systems require approximately 23 bytes.

·    Tail 去检查fracture block分裂块,例如本例使用BBED查看 file 1 block 1 p kebh时能看到 tail d23a0610,然后去检查块头的 scn 0002d23a, type 06, sequence 01由于windowslinux都是little ending的所以要从后往前那么分开后就是 scn+type+sequence= d23a0610

3. 重点解释Dump格式的5个部分

下面看看对应的DUMP文件信息来一一对应上边几部分:

Start dump data blocks tsn: 6 file#:5 minblk 16527 maxblk 16527

---------------------------------------------------------------

Block dump from cache:

Dump of buffer cache at level 4 for tsn=6, rdba=20988047

BH (0x187F655C) file#: 5 rdba: 0x0140408f (5/16527) class: 1 ba: 0x186D2000

  set: 3 bsz: 8192 bsi: 0 sflg: 2 pwc: 1334 lid: 0x00000000,0x00000000

  dbwrid: 0 obj: 71092 objn: 71092 tsn: 6 afn: 5

  hash: [0x34946C48,0x34946C48] lru: [0x187F668C,0x187F64EC]

  ckptq: [NULL] fileq: [NULL] objq: [0x187F654C,0x187F66EC]

  st: XCURRENT md: NULL tch: 2

  flags: block_written_once redo_since_read gotten_in_current_mode

  LRBA: [0x0.0.0] LSCN: [0x0.0] HSCN: [0xffff.ffffffff] HSUB: [5]

  cr pin refcnt: 0 sh pin refcnt: 0

注意:以上这一段只有11g


-----------------第一部分Cache Layer -------------------


buffer tsn: 6 rdba: 0x0140408f (5/16527)  

scn: 0x0000.00368e2c seq: 0x02 flg: 0x04 tail: 0x8e2c0602

frmt: 0x02 chkval: 0x78cf type: 0x06=trans data

--tsn: tablespace number只是dump文件中记录而已Block中是没有记录tns:select ts# from v$tablespace where name ='ETMCDB';

--rdba: 4 bytes 表示块在数据文件中的位置也就是第几个BLOCK。后五位可以换算得到BLOCK的位置可是?前面3位不知道怎么去换算得到数据文件的编号?不过这个地址可以使用dbms_utility.data_block_address_file或者dbms_utility.data_block_address_block来转换出来这个地址数据哪个文件的哪个BLOCK select GET_DBA_INFO('140408f') from dual;得到 RFILE_NO=11 BLOCK_NO=226

--scn:  6 bytes system change number select to_number('00368e2c', 'xxxxxxxxxxxxx') scn from dual; 和这个对应select dbms_flashback.get_system_change_number from dual;

只有当CHECKPOINT发生的时候,这里的SCN才会发生改变注意如果修改数据后没有CHECKPOINT就去DUMP,发现SCN号却发生了变化,那是因为DUMP出来的是脏的BUFFER CACHE中的数据,真正的物理文件中的数据可以通过BBED来查看.同时,可以到v$BH(x$bhx$le hash join的结果)中去查看这个BLOCK的状态,可以发现即使DUMP完成后,这个BLOCK还是DIRTY,说明这个BLOCK是从BUFFER直接DUMP出来的,而不是先把块写到文件中,然后再从文件DUMP出来

--seq:  1 byte  A sequence number incremented for each change to a block at the same SCN A new SCN is allocated if the

sequence number wraps.同一个SCN影响这个block中的行数大于254 行就会为这个事务分配一个新的SCN.如下面的操作就可能引起同一个SCN但影响的同一个block 中的行超过行 "delete from table_name" 影响的行数(最大) 是用从0x01 0xfe 表示的当这个byte 的数据为0xff的时候标志这个block坏了-> ora-01578

--flg:  1 byte 1 = virgin block

--             2 = last change to the block was for a cleanout operation

--             4 = checksum value is set

--             8 = temporary data

--             这是一个可以组合的值也就是说有为6 的时候是2,4 两种情况的组合

--tail: 4 bytes 这其实是最后面的4bytes的数据dump文件中只是把他写在了前面而已 lower scn(8e2c)+type(06)+seq(02)

--frmt: 1 byte  oracle 8 以后看见的都是0x02

--chkval: 2 bytes init 文件中设置了db_block_checksum=true 才有值

--type: 1 byte 这个block 的类型这里主要看0x06可以参考 http://www.ixora.com.au/notes/cache_block_types.htm

 


Hex dump of block: st=0, typ_found=1

Dump of memory from 0x186D2000 to 0x186D4000

内存地址   实际存储内容,每两位是一个字节                  内存中内容实际对应的ASCII

186D2000 0000A206 0140408F 00368E2C 04020000  [.....@@.,.6.....]

186D2010 000078CF 00000001 000115B4 00368E29  [.x..........).6.]

186D2020 00000000 00320002 01404089 000F0007  [......2..@@.....]

186D2030 0000077A 00C0096C 00070216 00000001  [z...l...........]

186D2040 00000000 00000000 00000000 00000000  [................]

        Repeat 1 times

186D2060 00000000 00010100 0014FFFF 0FD80FEC  [................]

186D2070 00000FD8 0FEC0001 00000000 00000000  [................]

186D2080 00000000 00000000 00000000 00000000  [................]

        Repeat 252 times #表示上面的一行内容在这里被重复了252#

186D3050 01010128 008E4040 0FA0FE00 31313131  [(...@@......1111]

186D3060 31313131 31313131 31313131 31313131  [1111111111111111]

        Repeat 248 times

186D3FF0 31313131 31313131 31313131 8E2C0602  [111111111111..,.]

上面这些就是从内存中DUMP出来的内容,最左边一列表示内存地址(可以看到第一行的地址跟上面描述的起始地址是一致的,而且每行存储16字节内容,那么内存地址每增加一行,计数都会进一位,因为16进制,就表示内存地址增加了16. 往右边的四列表示了实际存储的内容,每两位是一个字节,所以一共存储了16字节内容. 最右边括号中的内容表示内存中内容实际对应的ASCII(这个对应关系不那么准确,尤其是在块的头部有很多控制字符,往后面翻到数据存储的地方,这个对应关系会比较靠谱点)

-----------------第二部分Transaction Layer -------------------


--------Transaction Layer的第一部分固定长度的KTBBH_结构-------

Block header dump:  0x0140408f 这个跟前面的RDBA对应表示这个块是哪个数据文件的哪个块

 Object id on Block? Y

 seg/obj: 0x115b4  csc: 0x00.368e29  itc: 2  flg: E  typ: 1 - DATA

           ^               ^              ^       ^       ^

           |               |              |       |        ------typ: 1 = DATA 2 = INDEX

           |               |              |       --------------flg: O = On Freelist

           |               |              ---------------------itc: Number of ITL slots

           |               ----------------------------------csc: SCN of last block cleanout

           ------------------------------------------------Seg/Obj ID in Dictionary

     brn: 0  bdba: 0x1404089 ver: 0x01 opc: 0

     inc: 0  exflg: 0 这一行11g之前没有

--seg/obj: 4 bytes 数据字典的数据SEG/OBJ表示的是这个BLOCK所对应的OBJECTOBJECT_ID而不一定是真正的SEGMENT_ID,这里是16进制的对应sys.obj$.obj# 参考 http://zhang41082.itpub.net/post/7167/495078

select to_number('115b4','xxxxxxxx') from dual;
71092
select object_id from user_objects where object_name = 'T';
71092
select Name from obj$  where obj# = 71092;
T

--csc:     6 bytes Clean SCN: The SCN at which the last full cleanout was performed on the block也就是一般说的块的SCN,这个号只有当BUFFER中的块被刷到磁盘的时候才会更新,不然DUMP出来的CSC是不变的

--itc:     1 byte  下面的Itl事务条的个数8.1.7的文档上面说可以使用INITRANS 在建表的时候限制这个值的大小(max 255超过会报ORA-02207)

                   但要考虑block 的空间是否够, 表在iINITRANS default1,9.2.0INITRANS default2

                   Yong Huang 说有些时候发生ORA-00060可以把表的INITRANS 设置大点

                   ixora 上说当block的空间不够创建一个ITL的时候一样可能引起ORA-00054

--flg:     2 bytes 0表示这个块是在FREELIST上的如果是-表示不在FREELIST上。如果是ASSM会显示为Eixora 上说他占用2 bytes 但我下面的试验和他的结果有一定的出入 我观察到的情况是:

                   Object id on Block? Y

                   flg: O

                   ver: 0x01

                   上面的3项是用同一个byte 来表示的

--typ:     1 byte  1table; 2index. oracle进行查询的时候是根据obj$表中的情况来判断对象的类型的,不是根据这个typ

                   也就是说如果有一个表但改变表中block的这个标志位,一样可以查询出数据来,dump block 时会出错,

                   ORA-00600: 内部错误代码,自变量: [4555], [0], [], [], [], [], [], []错误中的[0] 就是typ对应的数据

--fsl:     1 byte  Index to the first slot on the ITL freelist.  ITL TX freelist slot

--fnx:     4 bytes 自由列表中下一块的地址Null if this block is not on a freelist 有数据例如: fnx: 0x1000029

--ver:     1 byte  format (version) 这个数据没有看到相关的文档介绍从ixora上说是占用byte 但我从下面的进制文件中看到的有不同下面有介绍

--unused:  4 bytes 在这里还有bytes 的空闲的空间但在上面的dump 文件上是没显示出来的 这个unused bytesixora 上面的说法

                   9i ASSM " fsl: 0  fnx: 0x0 ver: 0x01 "这一段数据的情况已经改变了

--------Transaction Layer的第二部分可变长度的KTBIT_结构-------

 Itl           Xid                  Uba         Flag  Lck        Scn/Fsc

0x01   0x0007.00f.0000077a  0x00c0096c.0216.07  ----    1  fsc 0x0000.00000000

0x02   0x0000.000.00000000  0x00000000.0000.00  ----    0  fsc 0x0000.00000000

这是oracle 用来记录事务信息的部分 这里显示的只有一个ITL条有多少个ITL条是可以动态增加的. 只要block中的空间足够 可以定义初始化的ITL 条的个数用INITRANS storage 参数, 这里有多少个ITL 可以从上面"第二部分" "itc:" 看出来, 这部分牵扯rollback segment undo tablespace

--- Itl  itl 的序号 参考http://www.ixora.com.au/q+a/datablock.htm#end

--- xid: 8 bytes 表示v$TRANSACTION中对应的事务号 select XIDUSN, XIDSLOT,XIDSQN from v$transaction; 回滚段号.事务槽.sequence

--- Uba: 8 bytes 表示事务在回滚段中对应的地址 第一部分表示回滚段块地址; 第二部分表示回滚块序号对应v$transaction UBASQN; 第三部分表示undo chainirb, 将指向具体使用的undo block. 其中第一部分可以使用dbms_utility.data_block_address_filedbms_utility.data_block_address_block转换得到这个事务对应的UNDO BLOCK的地址。

--- flag 1 nibble ---- = transaction is active, or committed pending cleanout

                     C--- = transaction has been committed and locks cleaned out

                     -B-- = this undo record contains the undo for this ITL entry 当读到这个ITL 的时候该block需要 rollback
                             (
一致读)导致ITL 来自 rollback segment也就是说该ITL事务被提交过多次了需要追述到最早的部分

                     --U- = transaction committed (maybe long ago); SCN is an upper bound表示该ITL事务已经提交但提交的SCNoracle在数据有效性的基础上出来的,可能并不是真实的commit scn这个SCN就是scn/fsc

                  ---T = transaction was still active at block cleanout SCN事务提交后ITL涉及到的数据行上的lock信息也就是lb,在下次dml操作到该块数据的时候lb会清零,表示清除lock (当然即使没有清除该ITL若已经提交也认为锁不存在)在这个清理的过程中相应flag位置会设置为 T

--- Lck  3 nibbles 表示该 itl 在该block上影响到的行数也就是这个事务所持有的行级锁的数目

--- Scn/Fsc 6 bytes If the transaction has been cleaned out, this is the commit SCN or an upper bound thereof.
                    Otherwise the leading two bytes contain the free space credit for the transaction - that is,

                    the number of bytes freed in the block by the transaction 这里还有一个延迟块清除的概念会影响到这里的内容

-----------------第三部分 Data Layer包括Data HeaderTable DirectoryRow DirectoryFree SpaceRow Data ---------------------------


data_block_dump,data header at 0xf364064 #这里说明了这个BLOCK的数据头从什么地方开始后面会用这个地址加上偏移来计算每行数据的地址#

===============

tsiz: 0x1f98  total data area size 除了上面的部分和block尾部的个字节剩下的空间0x1fb8就是字节

              8kblock: 8192-20(block head)-24(Transaction Header)-24(一个事务条)-4(block tail)=8120

hsiz: 0x14    data header size 数据块头个字节+数据块尾个字节=24字节(0x14)

pbl: 0x0f364064 point to buffer holding the block 

                我是用的专用模式dumpdatafile中的block出来在同一个sessiondump文件中dump出来的block 的这个都是同一个值 76543210

bdba: 0x01000003 rdba Header 中的 rdba 相同

flag=-------- N=pctfree hit(clusters), F=don't put on free list K=flushable cluster keys. 当然还有别的标记: A ...

ntab=1        1 byte     表示这个block中有几个table的数据  cluster这个就可能大于1

nrow=1        2 bytes    block 中有多少行数据

frre=-1       2 bytes    First free row index entry. -1=you have to add one.

fsbo=0x14     2 bytes    Free Space Begin offset 空闲空间从哪里开始

fseo=0xfec    2 bytes    Free Space End offset 空闲空间到哪里结束

avsp=0xfd8    2 bytes    Available space in the block

tosp=0xfd8    2 bytes    Total available space when all TXs commit 所有事物都提交后可使用的空间

0xe:pti[0]  nrow=1  offs=0 2 bytes    block 中的这个table有多少行数据

0x12:pri[0] offs=0xfec 2 bytes select to_number('oxfec','xxxxxxxx') from dual; 偏移量用cluster的时候可以看出值

-----------------第四部分Row Data-------------------

block_row_dump:

tab 0, row 0, @0xfe1 该行在数据块中的偏移地址 Row Directory 中的偏移地址对应

tl: 4031 fb: --H-F--N lb: 0x1 cc: 2

 ^      ^            ^       ^


 |      |            |       ------Number of columns in this ROW piece


 |      |            -------------Lock byte - ITL entry that has this row locked


 |      ------------------------Flag byte: H: Head of row piece , K: Cluster key


 |                                         C: Cluster table member, 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         


 --------------------------------Row Size(header + data)


nrid: 0x0700000a.0 可以找到下一个数据块

select dbms_utility.DATA_BLOCK_ADDRESS_FILE(to_number('700000a','xxxxxxxxxx')) file#,

   dbms_utility.DATA_BLOCK_ADDRESS_BLOCK(to_number('700000a','xxxxxxxxxx')) block#

  from dual;

     FILE#     BLOCK#

---------- ----------

        8         10

col 0: [4000]  

31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31

.................

 

col  1: [42]

 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32

 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 ? 为什么都是31,然后就是32,33 ?

end_of_block_dump

End dump data blocks tsn: 27 file#: 28 minblk 11 maxblk 11

end_of_block_dump

--- tab:  所在表的序号

--- row:  该行在块中的行号

--- @0xfe1: 该行在数据块中的偏移地址 Row Directory 中的偏移地址对应

--- tl:   rowsize(number of bytes plus data) 这条记录中的长度包括row head 的一般情况的3 字节和表示数据长度的字节和数据本身的长度

--- fb:   1 byte   K = Cluster Key (Flags may change meaning if this is set to show HASH cluster)

---                C = Cluster table member

---                H = Head piece of row

---                D = Deleted row

---                F = First data piece

---                L = Last data piece

---                P = First column continues from previous piece

---                N = Last column continues in next piece

--- lb:   1 byte   lock byte—itl entry then has this row locked 和上面第三部分的ITL lck相对应 表示这行是否被lock

--- cc:   1 byte   当前数据块中列的个数 推测CCCOLUMN COUNT的缩写

--- col0: [4000]   1 byte  表示这行的这列的长度

--- c1 02 :        这就是table中的数据"1"  可以通过下面的语句看oracle真正使用的是

---                那些进制的数据来表示的用户数据select dump(col_name,16) from table_name;

可以看到这个BLOCK并没有在整列的时候中断而是在这个块中写了部分COL2的数据。这就证实文章开头的推测。从上面红色的地址可以找到下一个BLOCK

select dump(rowid, 16) from t;

DUMP(ROWID,16)

------------------------------------------------------------------------

Typ=69 Len=10: 0,1,15,b4,1,40,40,8f,0,0

然后再Dump前一个块儿  

ALTER SYSTEM DUMP DATAFILE 5 BLOCK 16526; dump下一个数据块来验证

    系统已更改

无论是和当前记录的ROWID对比还是根据ROWID的存储格式特点都可以确定下一个BLOCK的位置对这个BLOCK进行DUMP再次观察

Start dump data blocks tsn: 6 file#: 5 minblk 16526 maxblk 16526

Start dump data blocks tsn: 0 file#: 1 minblk 61018 maxblk 61018

buffer tsn: 0 rdba: 0x0040ee5a (1/61018)

scn: 0x0000.0008bbee seq: 0x05 flg: 0x04 tail: 0xbbee0605

frmt: 0x02 chkval: 0xfb1c type: 0x06=trans data

Hex dump of block: st=0, typ_found=1

Dump of memory from 0x0D61E400 to 0x0D620400

Block header dump:  0x0040ee5a

 Object id on Block? Y

 seg/obj: 0xcd67  csc: 0x00.8bbeb  itc: 3  flg: -  typ: 1 - DATA

     fsl: 0  fnx: 0x0 ver: 0x01

 

 Itl           Xid                  Uba         Flag  Lck        Scn/Fsc

0x01   0x0008.013.00000131  0x00800da0.00ed.0b  ----    1  fsc 0x0000.00000000

0x02   0x0000.000.00000000  0x00000000.0000.00  ----    0  fsc 0x0000.00000000

0x03   0x0000.000.00000000  0x00000000.0000.00  C---    0  scn 0x0000.00000000

 

data_block_dump,data header at 0xd61e474

===============

tsiz: 0x1f88

hsiz: 0x14

pbl: 0x0d61e474

bdba: 0x0040ee5a

     76543210

flag=--------

ntab=1

nrow=1

frre=-1

fsbo=0x14

fseo=0x69

avsp=0x55

tosp=0x55

0xe:pti[0]      nrow=1  offs=0

0x12:pri[0]     offs=0x69

block_row_dump:

tab 0, row 0, @0x69

tl: 7967 fb: -----LP- lb: 0x1  cc: 2

col  0: [3958]测试一个数据块的最大容量是4000剩下的3958放在上一个数据块中

 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32

...............................

col  1: [4000]

 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33

...............................

end_of_block_dump

End dump data blocks tsn: 0 file#: 1 minblk 61018 maxblk 61018

看完这个BLOCK的结果,可以发现Oracle似乎是从后向前进行的写操作,Oracle存储数据是从后往前的,比如一行数据分配了 block #1,2,3,那么存储起来就是先写3,然后2,写不下了再写1 之所以这样说是因为这个块被写满了,而前一块并没有被写满。根据两个BLOCK结构的分析,基本上确认了文章开始的分析,也就是Oracle一个块无法写下全部记录的时候,会通过行链接实现,而实现的方式就是在块头处通过ROWID的方式之处下一个存放记录的块的位置。虽然这个问题解决了,但是还存在一个新的问题。前面提到了,Oracle并不是以列作为划分记录的单位,一列的数据可能分布到多个BLOCK上,比如这个例子。那么Oracle是如何确定当前BLOCK存放最后一列记录是否完整呢。每个BLOCK中,数据存放都是从COL0开始排序递增的,因此这个COL的编号是块内的编号,而不是记录内的列的编号,显然无法通过这个COL位置来确定列的信息。虽然这个例子本身没有疑问,因为表本身包含个列,而每个BLOCK都有两个列,显然是第二个列在第一个BLOCK中没有完全存储,部分内容写到了第二个BLOCK中。但是这种分析并不适用所有的情况,假设当前表就是个字段,那么根据DUMP的结果就没有办法判断是COL2被分散到了两个BLOCK,且COL4为空;还是COL2的长度就是,而COL3的长度是,COL4长度是。因此,Oracle肯定通过其他的手段记录了这个信息。

 

Oracle如何确定当前Block中列的数据是否已经完整

根据前面的例子中COL2列有42个字节存放在BLOCK1而剩余的3958个字节存放在第二个BLOCK中。而且根据前面的推测,这个BLOCK的存放似乎是从后向前,也就是说,第二个BLOCK放满后,才开始向第一个BLOCK中写。那么可以构造一个表,使得COL2的长度恰好满足写满第二个BLOCK,这里将长度变为3958,插入记录后,观察dump的结果:

create table t1 (col1 varchar2(4000), col2 varchar2(4000), col3 varchar2(4000)) tablespace ETMCDB;

insert into t1 values (lpad('1', 4000, '1'), lpad('2', 3958, '2'), lpad('3', 4000, '3'));

commit;

alter system checkpoint;

SELECT DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID), DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID) FROM T1;

DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID) DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)

------------------------------------ ------------------------------------

                                   6                                   16

ALTER SYSTEM DUMP DATAFILE 6 BLOCK 16;

Start dump data blocks tsn: 7 file#: 6 minblk 16 maxblk 16

buffer tsn: 7 rdba: 0x01800010 (6/16)

scn: 0x0000.00096ef1 seq: 0x03 flg: 0x04 tail: 0x6ef10603

frmt: 0x02 chkval: 0x0016 type: 0x06=trans data

Hex dump of block: st=0, typ_found=1

Block header dump:  0x01800010

 Object id on Block? Y

 seg/obj: 0xcdb7  csc: 0x00.96ef1  itc: 2  flg: E  typ: 1 - DATA

     brn: 0  bdba: 0x1800009 ver: 0x01 opc: 0

     inc: 0  exflg: 0

 

 Itl           Xid                  Uba         Flag  Lck        Scn/Fsc

0x01   0x0008.00b.00000147  0x00800dbe.00f0.1b  ----    1  fsc 0x0000.00000000

0x02   0x0000.000.00000000  0x00000000.0000.00  ----    0  fsc 0x0000.00000000

 

data_block_dump,data header at 0xe2f0464

===============

tsiz: 0x1f98

hsiz: 0x14

pbl: 0x0e2f0464

bdba: 0x01800010

     76543210

flag=--------

ntab=1

nrow=1

frre=-1

fsbo=0x14

fseo=0xfec

avsp=0xfd8

tosp=0xfd8

0xe:pti[0]      nrow=1  offs=0

0x12:pri[0]     offs=0xfec

block_row_dump:

tab 0, row 0, @0xfec

tl: 4012 fb: --H-F--- lb: 0x1  cc: 1 #这里COLUMN COUNT变成了 1#

-H-FL--  (2c0x20+0x08+0x04)

     0x20  Head piece of row

     0x08  First data piece

     0x04  Last data piece 这里没有L说明这个块不够用需要写入到另外一个块上

nrid:  0x0180000c.0

col  0: [4000]

 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31

..........................

 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31

end_of_block_dump

End dump data blocks tsn: 7 file#: 6 minblk 16 maxblk 16

 

create table t2(col1 varchar2(1000), col2 varchar2(1000), col3 varchar2(1000), col4 varchar2(1000), col5 varchar2(4000), col6 varchar2(4000)) tablespace ETMCDB;

insert into t2 values (lpad('1', 1000, '1'), lpad('2', 1000, '2'), lpad('3', 1000, '3'),lpad('4', 1000, '4'),lpad('5', 4000, '5'),lpad('6', 4000, '6'));

commit;

alter system checkpoint;

SELECT DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID), DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID) FROM T2;

ALTER SYSTEM DUMP DATAFILE 6 BLOCK 56;

tl: 4040 fb: --H-F--N lb: 0x1 cc: 5 这里COLUMN COUNT5说明存储了5columns

 


结论 对比两个数据块头

TBlock1信息                                                               TBlock2信息                                                        

Start dump data blocks tsn: 27 file#: 28 minblk 27 maxblk 27                   Start dump data blocks tsn: 27 file#: 28 minblk 26 maxblk 26         

buffer tsn: 27 rdba: 0x0700001b (28/27)                                            buffer tsn: 27 rdba: 0x0700001a (28/26)                              

scn: 0x0001.79f5e324 seq: 0x01 flg: 0x06 tail: 0xe3240601                       scn: 0x0001.79f5e324 seq: 0x01 flg: 0x06 tail: 0xe3240601            

frmt: 0x02 chkval: 0x5a86 type: 0x06=trans data                                  frmt: 0x02 chkval: 0x9e47 type: 0x06=trans data                      

Block header dump: 0x0700001b                                                       Block header dump: 0x0700001a                                        

Object id on Block? Y                                                                Object id on Block? Y                                                

seg/obj: 0x98eb csc: 0x01.79f5e321 itc: 2 flg: O typ: 1 - DATA                seg/obj: 0x98eb csc: 0x01.79f5e321 itc: 3 flg: - typ: 1 - DATA       

fsl: 0 fnx: 0x0 ver: 0x01                                                           fsl: 0 fnx: 0x0 ver: 0x01                                            

                                                                     

Itl Xid Uba Flag Lck Scn/Fsc                                                        Itl Xid Uba Flag Lck Scn/Fsc                                         

0x01 0x0009.001.00016d53 0x00910f26.075b.1f --U- 1 fsc 0x0000.79f5e324       0x01 0x0009.001.00016d53 0x00910f26.075b.1e --U- 1 fsc 0x0000.79f5e324

0x02 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000       0x02 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000

                                                                                         0x03 0x0000.000.00000000 0x00000000.0000.00 C--- 0 scn 0x0000.00000000

data_block_dump,data header at 0xb742d25c                                         data_block_dump,data header at 0xb742d274                                        

===============                                                                       ===============                                                      

tsiz: 0x1fa0                                                                          tsiz: 0x1f88                                                         

hsiz: 0x14                                                                             hsiz: 0x14                                                           

pbl: 0xb742d25c                                                                       pbl: 0xb742d274                                                      

bdba: 0x0700001b                                                                      bdba: 0x0700001a                                                     

76543210                                                                               76543210                                                             

flag=--------                                                                          flag=--------                                                        

ntab=1                                                                                  ntab=1                                                               

nrow=1                                                                                  nrow=1                                                               

frre=-1                                                                                 frre=-1                                                              

fsbo=0x14                                                                             fsbo=0x14                                                            

fseo=0xfd8                                                                            fseo=0x51                                                            

avsp=0xfc4                                                                            avsp=0x3d                                                             

tosp=0xfc4                                                                            tosp=0x3d                                                            

0xe:pti[0] nrow=1 offs=0                                                            0xe:pti[0] nrow=1 offs=0                                             

0x12:pri[0] offs=0xfd8                                                              0x12:pri[0] offs=0x51                                                

block_row_dump:                                                                      block_row_dump:                                                      

tab 0, row 0, @0xfd8                                                                tab 0, row 0, @0x51                                               

tl: 4040 fb: --H-F--N lb: 0x1 cc: 5                                                tl: 7991 fb: -----LP- lb: 0x1 cc: 2                          

nrid: 0x0700001a.0                                                         

仔细对比上面这些头信息,这些标识位就显得十分明显了。观察这些头信息的红色部分,这一系列的字母就标识了行链接的信息。可以看到所有的块头都包括HF,而所有的结尾块都包含L,那么显然H或者F标识的是行头信息,而L标识行尾信息。推测H这里表示HIGN END或者HEADINGL表示LOW END或者LEAF。如果当前块包含的最后一个COLUMN的内容没有结束会在标识中包含一个N应该表示NEXT的含义。而对应的下一个BLOCK,如果列不是从头开始,会在标识中包含一个P,应该是PRIOR的含义。

select segment_name,file_id,block_id,blocks from dba_extents where segment_name='T2'

SEGMENT_NAME    FILE_ID   BLOCK_ID     BLOCKS

--------------- ------ ---------- ----------

T1               5         7209          8

 

SELECT DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID), DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID) FROM T2;

DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID) DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)

------------------------------------ ------------------------------------

                                   6                                 7217

获取了第一块数据块的号码,就开始了dump要挖掘出所有存储数据的数据块

alter system dump datafile 5 block 7217;

获得了nrid: 0x01401c30 后比如我在这里问的问题http://yangtingkun.itpub.net/post/468/465376

select dbms_utility.DATA_BLOCK_ADDRESS_FILE(to_number('1401c30','xxxxxxxxxx')) file#,

       dbms_utility.DATA_BLOCK_ADDRESS_BLOCK(to_number('1401c30','xxxxxxxxxx')) block#

  from dual

     FILE#     BLOCK#

---------- ----------

         5       7216

alter system dump datafile 5 block 7216;

这样又找到了一个nrid:  0x01401c2f.0 继续

select dbms_utility.DATA_BLOCK_ADDRESS_FILE(to_number('1401c2f','xxxxxxxxxx')) file#,

       dbms_utility.DATA_BLOCK_ADDRESS_BLOCK(to_number('1401c2f','xxxxxxxxxx')) block#

  from dual

这时终于没有了nrid,说明数据被分布在个Block

这就是说明从block57开始一直数个就是,由于oracle是从后往前记录的,所以块头就是在,但是一般我dump两个数据块就dump完了所有的内容了那么其他的就空着? 哦难道是一下子就分个数据块那么就是一个extent? 说明一个extent只是用了个数据块其他个是空的

谢谢您的回复,根据这个nrid找到了下一个数据块,继续dump就得到了我要的结果,那么反向推理出这个extent是从开始分配的一直到,共个数据块.其中个数据块有数据,其他的dump不出,那么就是空块了.

select一次看来T2又插入了新数据所以分配了更多的Extent

select segment_name,file_id,block_id,blocks from dba_extents where segment_name='T2';

SEGMENT_NAME    FILE_ID   BLOCK_ID   BLOCKS

-------------------------- ------------- ---------------- ----------

T2                        5            7209          8

T2                        5            7185          8

SELECT DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID), DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID) FROM T2;

DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID) DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)

------------------------------------ ------------------------------------

                                   5                                 7192

                                   5                                 7192

                                   5                                 7192

                                   5                                 7192

                                   5                                 7192

                                   5                                 7192

                                   5                                 7192

                                   5                                 7192

                                   5                                 7192

                                   5                                 7192

....................................

已选择行。

里边的数据应该有行,所以又个rowid,验证下:

select count(*) from t2;

  COUNT(*)

----------

      1000

--EOF--

 

posted @ 2011-02-16 18:01  xxd  阅读(2280)  评论(0编辑  收藏  举报