一. ITL 说明
ITL: Interested Transaction List,也叫事务槽,它位于BLOCK Header。 先dump 一个block,看一下这个ITL 的信息。
SELECTa., extent_id
a., block_id
a.blocks, b
FROMa, segment_name file_id bfile# ;SELECT,nameFROM;
当某个对象比较大时,占用的block 也就会很多,Oracle 的存储结构是segment -->extent-->block. 每个extent 由一些block组成。 在上面的示例中每个extent 由8个blocks 组成。
我们dump 其中的一个Block:
SYS@anqing2(rac2)> alter system dump datafile 1 block 71553;
System altered.
SYS@anqing2(rac2)> oradebug setmypid
Statement processed.
SYS@anqing2(rac2)> oradebug tracefile_name
/u01/app/oracle/admin/anqing/udump/anqing2_ora_9114.trc
DUMP 的部分内容:
[oracle@rac2 ~]$ cat /u01/app/oracle/admin/anqing/udump/anqing2_ora_9114.trc
/u01/app/oracle/admin/anqing/udump/anqing2_ora_9114.trc
Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - Production
With the Partitioning, Real Application Clusters, OLAP, Data Mining
and Real Application Testing options
ORACLE_HOME = /u01/app/oracle/product/10.2.0/db_1
System name: Linux
Node name: rac2
Release: 2.6.18-194.el5
Version: #1 SMP Tue Mar 16 21:52:43 EDT 2010
Machine: i686
Instance name: anqing2
Redo thread mounted by this instance: 2
Oracle process number: 35
Unix process pid: 9114, image: oracle@rac2 (TNS V1-V3)
*** ACTION NAME:() 2011-06-28 21:06:13.112
*** MODULE NAME:(sqlplus@rac2 (TNS V1-V3)) 2011-06-28 21:06:13.112
*** SERVICE NAME:(SYS$USERS) 2011-06-28 21:06:13.112
*** SESSION ID:(141.4638) 2011-06-28 21:06:13.112
Start dump data blocks tsn: 0 file#: 1 minblk 71553 maxblk 71553
buffer tsn: 0 rdba: 0x00411781 (1/71553)
scn: 0x0000.0049ee36 seq: 0x01 flg: 0x04 tail: 0xee360601
frmt: 0x02 chkval: 0x3a9d type: 0x06=trans data
Hex dump of block: st=0, typ_found=1
Dump of memory from 0x0E862400 to 0x0E864400
......
E8643F0 C3040200 04036002 65766164 EE360601 [.....`..dave..6.]
Block header dump: 0x00411781
Object id on Block? Y
seg/obj: 0xd5ec csc: 0x00.49ee29 itc: 3 flg: - typ: 1 - DATA
fsl: 0 fnx: 0x0 ver: 0x01
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x000e.007.00000236 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 ---- 0 fsc 0x0000.00000000
可以看到ITL包含了SCN,undo地址,事物信息,事物影响该block的条数。
从上面的信息可以看到block中也会记录SCN的信息,每个ITL对应一个SCN,由于一个事物的操作可能会涉及到多个BLOCK的更改,所以一个事物可能在多个BLOCK中产生ITL信息。
一般来说,SCN 会保存在4个地方,控制文件中有三个,还有一个在datafile header.
有关这个问题,参考我之前的Blog:
Oracle 实例恢复时 前滚(roll forward) 后滚(roll back) 问题
http://blog.csdn.net/tianlesoftware/archive/2011/03/29/6286330.aspx
RedoLog Checkpoint 和 SCN关系
http://blog.csdn.net/tianlesoftware/archive/2010/01/24/5251916.aspx
ITL中另外一个相当重要的就是Uba,它记录了UNDO的信息,这个是consistant read的基础。
有关CR,参考Blog:
CR (consistent read) blocks create 说明
http://blog.csdn.net/tianlesoftware/archive/2011/06/07/6529401.aspx
当用户发出一条SQL语句,ORACLE就知道了它的结果,其实原因就在于ITL中记录的SCN,和Uba. 当发出一条sql语句时,ORACLE会记录下这个时刻(SCN),然后在buffer cache中查找需要的BLOCK,或者从磁盘上读,当别的会话修改了数据,或者正在修改数据,就会在相应的block上记录ITL,此时ORACLE发现ITL中记录的SCN大于SELECT时刻的SCN,那么ORACLE就会根据ITL中的Uba找到UNDO信息获得该block的前镜像,然后在buffer cache 中构造出CR块,此时ORALCE 也会检查构造出来的BLOCK中ITL记录的SCN,如果SCN还大于select时刻的SCN,那么一直重复构造前镜像,然后ORACLE找到前镜像BLOCK中的ITL的SCN是否小于select的SCN,同时检查这个事物有没有提交或者回滚,如果没有,那么继续构造前镜像,直到找到需要的BLOCK,如果在构造前镜像的过程中所需的UNDO信息被覆盖了,就会报快照过旧的错误,这样ORACLE就实现了多版本,这就是ORACLE多版本的本质,我们现在也知道了为什么发出一条select 语句总是会看到consistant gets了。
ITL也是要占用空间的,它存在于block 头部。在ORACLE10g中,如果建表的时候不指定initrans参数,那么默认的itl数为2,最大上限为255,同时指定maxtrans参数会被忽略(9i中有效),在BLOCK有足够空间的情况下,ORACLE会在需要的情况下自动分配ITL。
对于热点表,可以适当设置较大的pctfree,以保证ORACLE能够有足够空间分配ITL。
PCTFREE: =BLOCK中空闲的大小/BLOCK总大小*100
10g中默认的pctfree为10,ORACLE会为一个BLOCK保留10%的空间以确保将来对于BLOCK中的数据的更新,如果这个值设置过小,可能会产生行迁移,导致性能下降.
下面做个试验,验证ITL与PCTFREE的关系
SYS@anqing2(rac2)> create table t1(a number,b varchar2(20)) pctfree 0;
Table created.
SYS@anqing2(rac2)> begin
2 for i in 1..1000 loop
3 insert into t1 values(i,'DBA');
4 end loop;
5 commit;
6 end;
7 /
PL/SQL procedure successfully completed.
SYS@anqing2(rac2)> select distinct dbms_rowid.rowid_block_number(rowid) from t1;
DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)
------------------------------------
73546
73547
由此可知t1表占用了2个block.
Session 1 中操作:
SYS@anqing2(rac2)> update t1 set b='I am DBA' where a<10;
9 rows updated.
Session 2 中操作:
SYS@anqing2(rac2)> update t1 set b='I am DBA' where a>10 and a<20;
9 rows updated.
Session 3 中操作:
SYS@anqing2(rac2)> update t1 set b='I am DBA' where a>20 and a<30;
-- 此时Session 的DML 操作hang住了
查看当前等待事件 v$session_wait 视图
SYS@anqing2(rac2)> select event from v$session_wait where event like '%ITL%';
EVENT
----------------------------------------------------------------
enq: TX - allocate ITL entry
此时出现了分配ITL条目的等待,因为PCTFREE为0,BLOCK中没有足够空间分配ITL,ORACLE只能重用ITL,但是这个时候由于没有COMMIT,无法重用ITL,所以会出现allocate ITL 等待事件,要解决此类问题,可以从新创建表,增大PCTFREE的值,或者更改应用设计,减少竞争。
二. 根据ITL找数据的前镜像
在第一节讲到了ITL 保存了SCN 和Unto的信息。 在这里,我们就测试一下通过ITL 找到对应的Undo 内容。
2.1 准备测试数据
--查看a=88的记录
SYS@anqing2(rac2)> select * from t1 where a=88;
A B
---------- --------------------
88 DBA
-- 查看a=88 记录存储的数据块位置
SYS@anqing2(rac2)> select dbms_rowid.rowid_relative_fno(rowid)file_id,dbms_rowid.rowid_block_number(rowid)block_id from t1 where a=88;
FILE_ID BLOCK_ID
---------- ----------
1 73546
--更新a=88的数据库,不要提交
SYS@anqing2(rac2)> update t1 set a=88888 where a=88;
1 row updated.
-- dump 该数据块
SYS@anqing2(rac2)> alter system dump datafile 1 block 73546;
System altered.
--使用oradebug 查看当前trace 文件位置
SYS@anqing2(rac2)> oradebug setmypid
Statement processed.
SYS@anqing2(rac2)> oradebug tracefile_name
/u01/app/oracle/admin/anqing/udump/anqing2_ora_32547.trc
在trace 文件里找到我们的ITL:
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x000e.02a.0000026f 0x01400117.0066.0c ---- 1 fsc 0x0001.00000000
0x02 0x000b.007.00000276 0x014000e8.005c.14 C--- 0 scn 0x0000.004fcd83
因为我们执行了一条update 操作,并且没有commit,那么这里的lock 为1,即Lck值为1. 即对应我们ITL 值为0x01 的记录。
SYS@anqing2(rac2)> select xidusn,xidslot,xidsqn,ubafil,ubablk,ubasqn,ubarec from v$transaction;
xidusn xidslot xidsqn ubafil ubablk ubasqn ubarec
---------- ---------- ---------- ---------- ---------- ---------- ----------
14 42 623 5 279 102 12
2.2 验证XID
ITL中的XID的格式为: usn#.slot#.wrap#
我们找到ITL 为0X011中的XID=0x000e.02a.0000026f
SYS@anqing2(rac2)> select to_number('000e','XXXXX') from dual;
TO_NUMBER('000E','XXXXX')
-------------------------
14
SYS@anqing2(rac2)> select to_number('02a','XXXXX')from dual;
TO_NUMBER('02A','XXXXX')
------------------------
42
SYS@anqing2(rac2)> select to_number('0000026f','XXXXXXXXX')from dual;
TO_NUMBER('0000026F','XXXXXXXXX')
---------------------------------
623
SYS@anqing2(rac2)>
这个和V$TRANSACTION中记录的XIDUSN,XIDSLOT,XIDSQN相同。
2.3 验证uba
UBA的格式为:DBA.seq#.rec#
ITL 为0x01 记录中的UBA=0x01400117.0066.0c, 其中01400117 表示的DBA(Data Block Address)的地址。
有关DBA 的内容,参考我的Blog:
Oracle rdba和 dba 说明
http://blog.csdn.net/tianlesoftware/archive/2011/06/07/6529346.aspx
将DBA 解析成file_id 和 block_id.
SYS@anqing2(rac2)> select dbms_utility.data_block_address_file(to_number('01400117','xxxxxxxxxxxx'))file_id,dbms_utility.data_block_address_block(to_number('01400117','xxxxxxxxxxxx'))block_id from dual;
FILE_ID BLOCK_ID
---------- ----------
5 279
SYS@anqing2(rac2)> select to_number('0066','xxxx') from dual;
TO_NUMBER('0066','XXXX')
------------------------
102
SYS@anqing2(rac2)> select to_number('0c','xxxx') from dual;
TO_NUMBER('0C','XXXX')
----------------------
12
这个和v$transaction 相同。
2.4 dump undo回滚段,找到更改的前镜像
-- dump undo block
SYS@anqing2(rac2)> alter system dump datafile 5 block 279;
System altered.
--get trace file
SYS@anqing2(rac2)> oradebug setmypid
Statement processed.
SYS@anqing2(rac2)> oradebug tracefile_name
/u01/app/oracle/admin/anqing/udump/anqing2_ora_32547.trc
在dump 文件中找到xid 为 0x000e.02a.0000026f 的记录。
小技巧:
这个文件很大,查找很麻烦,我们可以使用vi 的查找功能来实现。
(1)vi /u01/app/oracle/admin/anqing/udump/anqing2_ora_32547.trc
(2)输入:/0x000e.02a.0000026f
(3)如果只有一条记录,一次就能匹配到,如果有多条记录,我们按字母n,查找下一条,知道找到结果位置。
查找的信息如下:
UNDO BLK:
xid: 0x000e.02a.0000026f seq: 0x66 cnt: 0xc irb: 0xc icl: 0x0 flg: 0x0000
该数据块的uba =0x01400117.0066.0c,UBA的格式:DBA.seq#.rec#。 这里的cnt对应uba中的rec#。 根据这个cnt 序列号号,我们来查找对应的具体内容。 方法和上面的一样,在vi中搜索: Rec #0xc, 搜到结果如下:
*-----------------------------
* Rec #0xc slt: 0x2a objn: 54770(0x0000d5f2) objd: 54770 tblspc: 0(0x00000000)
* Layer: 11 (Row) opc: 1 rci 0x0b
Undo type: Regular undo Last buffer split: No
Temp Object: No
Tablespace Undo: No
rdba: 0x00000000
*-----------------------------
KDO undo record:
KTB Redo
op: 0x02 ver: 0x01
op: C uba: 0x01400117.0066.0a
KDO Op code: ORP row dependencies Disabled
xtype: XA flags: 0x00000000 bdba: 0x00411f4a hdba: 0x00411f49
itli: 1 ispac: 0 maxfr: 4863
tabn: 0 slot: 87(0x57) size/delt: 10
fb: --H-FL-- lb: 0x1 cc: 2
null: --
col 0: [ 2] c1 59
col 1: [ 3] 44 42 41
SYS@anqing2(rac2)> set serveroutput on
SYS@anqing2(rac2)> declare n number;
2 begin
3 dbms_stats.convert_raw_value('c159',n);
4 dbms_output.put_line(n);
5 end;
6 /
88 -- 输出结果为88,我们修改之前的记录
PL/SQL procedure successfully completed.
通过这个测试,清楚了Oracle 通过ITL 找到数据的前镜像了。
三. ITL 与defered Clean (延时块清除)
A "deferred (CURRENT)" cleanout occurs when a DML statement wants to modify the contents of a block and all the available ITLs are showing recent commit cleanouts. The process cleans out the block (incrementing the statistic once) and then uses the ITL slot with the lowest commit SCN.
For example if there are some ITL slots available for use (empty, or showing older, cleaned commits) but the process wants to modify a row that is still marked as locked by a commit cleanout (which don't clear lock bytes), then the process does a full cleanout (again incrementing the statistic) to get the row's lock byte cleared. It won't use the newly cleaned ITL, though if there are more appropriate ITLs (lower commit SCN) available in the block.
在块清除过程中,如果一个块已被修改,下一个会话访问这个块时,可能必须查看最后一个修改这个块的事务是否还是活动的。一旦确定该事务不再活动,就会完成块清除,这样另一个会话访问这个块时就不必再历经同样的过程。
要完成块清除,Oracle 会从块首部确定前一个事务所用的undo 段(ITL),然后确定从undo 首部能不能看出这个块是否已经提交。
可以用以下两种方式完成这种确认:
一种方式是Oracle 可以确定这个事务很久以前就已经提交,它在undo 段事务表中的事务槽已经被覆盖。
另一种情况是COMMIT SCN 还在undo 段的事务表中,这说明事务只是稍早前刚提交,其事务槽尚未被覆盖。
当满足以下条件时,就会从defered clean 收到ORA-01555的错误:
首先做了一个修改并COMMIT,块没有自动清理(即没有自动完成“提交清除”,例如修改了太多的块,在SGA 块缓冲区缓存的10%中放不下)。
其他会话没有接触这些块,而且在我们这个“倒霉”的查询(稍后显示)命中这些块之前,任何会话都不会接触它们。
开始一个长时间运行的查询。这个查询最后会读其中的一些块。这个查询从SCN t1 开始,这就是读一致SCN,必须将数据回滚到这一点来得到读一致性。
开始查询时,上述修改事务的事务条目还在undo 段的事务表中。查询期间,系统中执行了多个提交。执行事务没有接触执行已修改的块(如果确实接触到,也就不存在问题了)。
由于出现了大量的COMMIT,undo 段中的事务表要回绕并重用事务槽(ITL)。最重要的是,将循环地重用原来修改事务的事务条目。另外,系统重用了undo 段的区段,以避免对undo 段首部块本身的一致读。
此外,由于提交太多,undo 段中记录的最低SCN 现在超过了t1(高于查询的读一致SCN)。如果查询到达某个块,而这个块在查询开始之前已经修改并提交,就会遇到麻烦。正常情况下,会回到块所指的undo 段,找到修改了这个块的事务的状态(换句话说,它会找到事务的COMMIT SCN)。
如果这个COMMIT SCN 小于t1,查询就可以使用这个块。如果该事务的COMMIT SCN 大于t1,查询就必须回滚这个块。不过,问题是,在这种特殊的情况下,查询无法确定块的COMMIT SCN 是大于还是小于t1。相应地,不清楚查询能否使用这个块映像。这就导致了ORA-01555 错误。
大批量的UPDATE 或INSERT 会导致块清除(block cleanout),所以在大批量UPDATE 或大量加载之后使用DBMS_STATS收集相关对象的统计信息,加载之后完成这些对象的清理。
有关快照过旧的更多内容,参考:
Oracle ORA-01555快照过旧 说明
http://blog.csdn.net/tianlesoftware/archive/2009/10/30/4745898.aspx
Block的cache header部分,记录着一个block scn,它是当前块最后一次变更的时间戳(确切说,这个更新并不是指itl上的scn的最新更新,在接下来delayed block cleanout下的slot重用情况下,可以看到blockscn并不等于itl上的最后一次更新的scn)。可以通过dump获得block scn/last itl scn 和发布ora_rowscn语句获得last itl scn。
发布transaction后,未提交之前,block scn是不会改变的,对应的itl中也并不做scn记录。Block scn的改变,确切的说不是在发布commit之时(因为有delayed block cleanout的情况存在),而是在transaction对应的itl获得commit scn之时。
当发生fast commit cleanout,系统将transaction提交时刻的scn作为commit scn,更新block上 itl和undo segment header的Transaction table的slot上的 scn,并修改block scn,三者是一致的。
发生delayed block cleanout的时候,之前的transaction commit更新的只是Transaction table,而并未做block上的处理,等待下次使用此block的时候,更新block scn和itl状态。block scn和itl的更新又分2种情况:
(1)当不产生slot重用的时候(ITL不重用), delayed block cleanout时,根据Transaction table里面的信息,更新block scn和itl上的Scn/Fsc为transaction曾经提交时候的scn。
(2)当产生slot重用的时候(重用ITL),更新对应itl上scn为control scn,而block scn 为delayed block cleanout发生时刻的scn。
dump block的ITL 的信息如下:
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x000e.007.00000236 0x00000000.0000.00C-U- 0 scn 0x0000.005b1f7f
0x02 0x000c.005.000003b4 0x01401727.0144.13C--- 0 scn 0x0000.005bbf0b
0x03 0x0011.007.00000406 0x0140015b.00c7.57--U- 483 fsc 0x0000.005bdee1
这里的SCN和FSC其实就是这个ITL对应的事务提交时候的SCN,那么这里所有槽位上的最大的一个SCN号就表示这个BLOCK最后被更新的时候的SCN。每一个事务对应一个itl 记录。 如果该事务没有涉及延时块清除,那么显示的FSC。 如果是延时块清除(delayed block cleanout),那么显示的就是SCN。
在ITL信息中有一个显示的Flag的状态,FLAG在block中占用1个字节大小。 不同flag 标记的意义如下:
---- = transaction is active, or committedpending cleanout
C--- = transaction has been committed andlocks cleaned out
-B-- = this undo record contains the undofor this ITL entry
--U- = transaction committed (maybe longago); SCN is an upper bound
---T = transaction wasstill active at block cleanout SCN
说明:根据如下Blog,重新整理:
http://blog.csdn.net/robinson1988/archive/2009/10/24/4721605.aspx
http://blog.csdn.net/robinson1988/archive/2009/12/21/5046712.aspx
-------------------------------------------------------------------------------------------------------
Blog: http://blog.csdn.net/tianlesoftware
Email: [email protected]
DBA1 群:62697716(满); DBA2 群:62697977(满) DBA3 群:62697850(满)
DBA 超级群:63306533(满); DBA4 群: 83829929 DBA5群: 142216823
DBA6 群:158654907 聊天 群:40132017 聊天2群:69087192
--加群需要在备注说明Oracle表空间和数据文件的关系,否则拒绝申请