基础知识

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

相关文章:

  • 2021-05-16
  • 2022-12-23
  • 2022-12-23
  • 2021-08-13
  • 2021-09-19
  • 2022-02-10
  • 2022-01-08
  • 2022-12-23
猜你喜欢
  • 2021-09-19
  • 2022-01-07
  • 2021-11-25
  • 2021-12-17
  • 2021-10-29
  • 2022-12-23
相关资源
相似解决方案