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数据块block是Oracle使用和分配的最小存储单位.它是由数据库建立时设置的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 增加1个IO开销。关于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 搭建环境查看Extent和Block信息
首先建表,插入数据,随后激活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
实际上数据只占了5个Block
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 的进制存储格式
--但这内存中的进制格式和datafile中block数据表示的顺序会略有不同可能是内存寻址的缘故吧
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由于windows和linux都是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$bh与x$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 这其实是最后面的4个bytes的数据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所对应的OBJECT的OBJECT_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 的空间是否够, 表在i中INITRANS default为1,9.2.0中INITRANS default为2
Yong Huang 说有些时候发生ORA-00060可以把表的INITRANS 设置大点
ixora 上说当block的空间不够创建一个ITL的时候一样可能引起ORA-00054
--flg: 2 bytes 0表示这个块是在FREELIST上的,如果是-表示不在FREELIST上。如果是ASSM的,会显示为E。ixora 上说他占用2 bytes 但我下面的试验和他的结果有一定的出入 我观察到的情况是:
Object id on Block? Y
flg: O
ver: 0x01
上面的3项是用同一个byte 来表示的
--typ: 1 byte 1为table; 2为index. 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 的bytes是ixora 上面的说法
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 chain(irb), 将指向具体使用的undo block. 其中第一部分可以使用dbms_utility.data_block_address_file和dbms_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事务已经提交但提交的SCN是oracle在数据有效性的基础上“猜”出来的,可能并不是真实的commit scn。这个SCN就是scn/fsc
---T = transaction was still active at block cleanout SCN。事务提交后ITL涉及到的数据行上的lock信息也就是lb,在下次dml操作到该块数据的时候lb会清零,表示清除lock。 (当然即使没有清除该ITL若已经提交也认为锁不存在)在这个清理的过程中相应flag