Chinaunix首页 | 论坛 | 博客
  • 博客访问: 37667
  • 博文数量: 6
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 15
  • 用 户 组: 普通用户
  • 注册时间: 2014-03-31 22:01
文章分类
文章存档

2017年(2)

2015年(4)

我的朋友

分类: Oracle

2015-09-15 14:07:40

原文地址:undo与事务剖析 作者:digdeep126

1. Oracle中的事务:
SQL> select xid,xidusn,xidslot,xidsqn,ubablk,ubafil from v$transaction;
XID                  XIDUSN    XIDSLOT     XIDSQN     UBABLK     UBAFIL
---------------- ---------- ---------- ---------- ---------- ----------
0200160000020000          2         22        512       2968          2

SQL> select * from v$rollname where usn=2;
       USN NAME
---------- ------------------------------
         2 _SYSSMU2$

xid表示事务ID,xidusn表示事务undo semgment number, xidslot表示事务槽,xidsqn表示sequence(覆盖次数),
其实xid有:xidusn, xidslot, xidsqn三部分组成。
ubablk表示undo block address block_no, 即回滚块编号;
ubafil表示undo block address file_no,即回滚块所在的文件编号;

我们知道每一个数据块在块头部有两个重要的内容:事务槽,行目录
事务槽用于保存修改该数据块的事务的相关信息。
SQL> select ini_trans,max_trans from dba_tables where table_name='TEST_INDEX';
 INI_TRANS  MAX_TRANS
---------- ----------
         1        255
事务槽的数量初始值为1,最多可以有255个.

事务槽的争用
每一个修改该数据块的事务,都需要占用一个事务槽。只有当事务提交时,该事务占用的事务槽才能被覆盖。我们知道pctfree=10%,那么当多个事务来修改该数据块时,需要多个事务槽,但是可能没有了空间,就必须等待其他的事务提交,新的事务才能开始。这就是所谓的事务槽的争用。而多个事务并行运行一些insert语句时,为了避免事务槽争用,可以才起插入到不同的数据块来减少事务槽的争用。但是并行事务的delete和update导致的事务槽争用却没有方法减少。

根据上面的信息我们可以对 回滚块和回滚段块头进行dump,然后进行研究:
alter system dump undo header '_SYSSMU2$';
alter system dump datafile 2 block 2968;
下图是一个数据块dump得到的头部的ITL事务槽
Itl: 事务槽编号; 
xid: 事务ID;其中包含了xidusn, xidslot, xidsqn,所以xid指向了事务表的一个条目;
uba: 回滚块地址;(数据块中的uba在构造CR块时使用;而事务表中的uba在rollback时使用)
flag: 事务是否提交的标识;用于实现行级锁;
lck:该事务锁定了几行数据;
scn: 前面我们知道每一条日志记录有一个scn;

注意:上面的ITL中的事务信息,同时也存在于undo段的事务表中

快速提交:普通的提交一般要修改多个地方:undo段的事务表、数据块头部的事务槽、数据行头部的标记等等。但是快速提交是:当一个事务涉及到要修改很多个数据块时,为了避免要在每个数据块头部都进行修改,而仅仅只修改undo段的事务表的提交标识。所以只有undo段的事务表中的提交信息才是最准确的

select语句也可能产生redo log:当select语句扫描到一行,发现行头部有锁标识,然后找到该行对应的事务槽中的事务信息,如果提交标识没有显示提交,然后再根据xid找到事务表,如果其中的事务信息,显示该事务已经提交,则select语句会将事务槽中的提交标识和数据行头部的锁标识分别设置为提交和null。所以就产生了redo log信息。

行级锁:数据块中的每一行数据的头部有一个事务槽编号的字段,当该行被一个事务修改时,则在该字段填上该事务对应的在事务槽中的ITL编号,来表示该行被锁定了。如果该字段为null则该行没有被锁定。当然,如果该字段不为null时,也不能肯定该行被锁定了,还要看事务槽中的提交标记,甚至还要看事务表中的提交标识。因为快速提交时,有可能只修改事务表中的提交标识。
创建一个测试表格:
SQL> create table t2(id number, name varchar2(32));
Table created.

SQL> insert into t2 values(1, 'a');
1 row created.

SQL> insert into t2 values(2, 'b');
1 row created.

SQL> commit;
Commit complete.

开始一个事务,产生了行锁:
SQL> update t2 set name='abc' where id=2;
1 row updated.

然后换一个session执行下面的操作,避免因为执行一些语句而将update语句的事务字段提交了。

SQL> select t2.*,dbms_rowid.rowid_relative_fno(rowid) fno,dbms_rowid.rowid_block_number(rowid) bno from t2;
        ID NAME                                    FNO        BNO
---------- -------------------------------- ---------- ----------
         1 a                                         1      64370
         2 b                                         1      64370

将数据块dump出来:
SQL> alter system dump datafile 1 block 64370
System altered.

SQL> select spid from v$session s inner join v$process p on s.paddr=p.addr inner join
   v$mystat m on s.sid=m.sid and m.statistic#=0;
SPID
------------
29879

SQL> show parameter user_dump;
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------------
user_dump_dest                       string      /u01/app/oracle/admin/jiagulun/udump

[oracle@localhost udump]$ pwd
/u01/app/oracle/admin/jiagulun/udump
[oracle@localhost udump]$ ls *29879*
jiagulun_ora_29879.trc
[oracle@localhost udump]$ cat jiagulun_ora_29879.trc
... ...
Block header dump:  0x0040fb72
 Object id on Block? Y
 seg/obj: 0xcfa7  csc: 0x00.103367  itc: 2  flg: O  typ: 1 - DATA
     fsl: 0  fnx: 0x0 ver: 0x01

 Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01    0x0003.006.00000219  0x0080011f.01b7.24  C---    0  scn 0x0000.00103250
0x02   0x0004.015.000001e7  0x00800261.01a1.27  ----    1  fsc 0x0000.00000000

data_block_dump,data header at 0xcc2b45c
===============
tsiz: 0x1fa0
hsiz: 0x16
pbl: 0x0cc2b45c
bdba: 0x0040fb72
     76543210
flag=--------
ntab=1
nrow=2
frre=-1
fsbo=0x16
fseo=0x1f62
avsp=0x1f77
tosp=0x1f77
0xe:pti[0]      nrow=2  offs=0
0x12:pri[0]     offs=0x1f98
0x14:pri[1]     offs=0x1f62
block_row_dump:
tab 0, row 0, @0x1f98
tl: 8 fb: --H-FL-- lb: 0x0  cc: 2
col  0: [ 2]  c1 02
col  1: [ 1]  61
tab 0, row 1, @0x1f62
tl: 10 fb: --H-FL-- lb: 0x2  cc: 2
col  0: [ 2]  c1 03
col  1: [ 3]  61 62 63
end_of_block_dump
End dump data blocks tsn: 0 file#: 1 minblk 64370 maxblk 64370
EXEC #3:c=15997,e=16422,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,tim=1319661474282543
=====================
我们将 update t2 set name='abc' where id=2 语句的数据块出来。从dump出来的数据可以看出:
1) lb: 0x0 表示第一行数据头部的锁定标志为0,所以没有被锁定;
2)lb: 0x2 表示第二行数据头部的锁定标志为2,它表示ITL事务槽的第二条事务信息;而第二条事务信息
的flag为空,表示没有提交,所以该行被锁定了(当然我们开需要查看事务表中的提交标志)。Lck=1表示锁定了1行数据

uba: 0x00800261.01a1.27 由三部分组成:
1)0x00800261是真正的uba地址;
2)01a1是XIDSQN;
3)27表示是undo记录中的0x27条记录

SQL> select xid,xidusn,xidslot,xidsqn,ubablk,ubafil from v$transaction;
XID                  XIDUSN    XIDSLOT     XIDSQN     UBABLK     UBAFIL
---------------- ---------- ---------- ---------- ---------- ----------
04001500E7010000          4         21        487        609          2

我们可以验证:
SQL> select to_number('00800261','xxxxxxxx') from dual;
TO_NUMBER('00800261','XXXXXXXX')
--------------------------------
                         8389217

SQL> select dbms_utility.data_block_address_file(8389217) from dual;
DBMS_UTILITY.DATA_BLOCK_ADDRESS_FILE(8389217)
---------------------------------------------
                                            2

SQL> select dbms_utility.data_block_address_block(8389217) from dual;
DBMS_UTILITY.DATA_BLOCK_ADDRESS_BLOCK(8389217)
----------------------------------------------
                                           609

然后执行 alter system dump datafile 2 block 609; 就可以将undo块dump出来:

Start dump data blocks tsn: 1 file#: 2 minblk 609 maxblk 609
buffer tsn: 1 rdba: 0x00800261 (2/609)
scn: 0x0000.00103364 seq: 0x01 flg: 0x04 tail: 0x33640201
frmt: 0x02 chkval: 0x276e type: 0x02=KTU UNDO BLOCK
Hex dump of block: st=0, typ_found=1
Dump of memory from 0x0CC2B400 to 0x0CC2D400
... ...
********************************************************************************
UNDO BLK:
xid: 0x000a.025.000001e6  seq: 0x1d4 cnt: 0x33  irb: 0x33  icl: 0x0   flg: 0x0000

 Rec Offset      Rec Offset      Rec Offset      Rec Offset      Rec Offset
---------------------------------------------------------------------------
0x01 0x1f48     0x02 0x1e90     0x03 0x1df0     0x04 0x1d48     0x05 0x1cd4
0x06 0x1c44     0x07 0x1b9c     0x08 0x1b04     0x09 0x1a6c     0x0a 0x19e8
0x0b 0x195c     0x0c 0x18d8     0x0d 0x185c     0x0e 0x1788     0x0f 0x1704
0x10 0x1664     0x11 0x15ac     0x12 0x1510     0x13 0x142c     0x14 0x13c4
0x15 0x136c     0x16 0x12e8     0x17 0x1280     0x18 0x11bc     0x19 0x10c8
0x1a 0x1030     0x1b 0x0f94     0x1c 0x0e9c     0x1d 0x0e04     0x1e 0x0d4c
0x1f 0x0cc4     0x20 0x0bfc     0x21 0x0b74     0x22 0x0aac     0x23 0x0a24
0x24 0x0978     0x25 0x0904     0x26 0x0858     0x27 0x07dc     0x28 0x0730
0x29 0x06b4     0x2a 0x0608     0x2b 0x058c     0x2c 0x04e0     0x2d 0x0464
0x2e 0x03b8     0x2f 0x033c     0x30 0x0290     0x31 0x0214     0x32 0x0168
0x33 0x00ec

... ... 
*-----------------------------
* Rec #0x27  slt: 0x15  objn: 53159(0x0000cfa7)  objd: 53159  tblspc: 0(0x00000000)
*       Layer:  11 (Row)   opc: 1   rci 0x00
Undo type:  Regular undo    Begin trans    Last buffer split:  No
Temp Object:  No
Tablespace Undo:  No
rdba: 0x00000000
*-----------------------------
uba: 0x00800261.01a1.26 ctl max scn: 0x0000.00102c89 prv tx scn: 0x0000.00102cdb
txn start scn: scn: 0x0000.00103234 logon user: 0
 prev brb: 8389214 prev bcl: 0
KDO undo record:
KTB Redo
op: 0x04  ver: 0x01
op: L  itl: xid:  0x000a.017.000001e6 uba: 0x008002b8.01d4.12
                      flg: C---    lkc:  0     scn: 0x0000.00102ee8
KDO Op code: URP row dependencies Disabled
  xtype: XA flags: 0x00000000  bdba: 0x0040fb72  hdba: 0x0040fb71
itli: 2  ispac: 0  maxfr: 4863
tabn: 0 slot: 1(0x1) flag: 0x2c lock: 0 ckix: 0
ncol: 2 nnew: 1 size: -2
col  1: [ 1]  62

End dump data blocks tsn: 1 file#: 2 minblk 609 maxblk 609

分析如下:
Rec #0x12 表示第0x12条undo记录;
slt: 0 x15  表示slot=0x15=21 与上面从v$transaction中查出来的一致;
col  1: [ 1]  62 表示具体的undo数据,即update之前的数据'b';

我们感性地看到了undo的记录条目,以及如何保持修改之前的数据。我们从数据块,

2. undo段的组成:
undo段是自动管理的,可以分为:回滚段段头块、回滚块。
段头块中有一个十分重要的概念:事务表,事务表用于保存使用该undo段的事务的信息,比如xid, uba等。事务表有47行,所以每一个回滚段最多可以47个事务来使用。

当一个事务开始时,会找到一个undo段,然后在段头块中的事务表中,找到一行填上xid, uba等信息。然后在该undo段中分配undo块(回滚块),用来保存被该事务修改之前的数据。
同时,会在ITL事务槽中也保存xid, uba等事务信息。最后才是修改数据。所以事务中的一条DML语句会涉及到四处修改地方(事务表、事务槽、回滚块、数据块),注意所有这些修改都会产生redo log信息

三个概念:事务表、回滚块、事务槽
事务表、事务槽(存放事务信息:xid 事务ID, uba 回滚块的地址);
回滚块(存放回滚数据)。

3. xid事务ID的组成:
xid既是一个事务ID,它其中包含了三部分的信息:
1)回滚段的段头块编号,
2)事务表中47行中的哪一行,
3)以及是第几次被使用/覆盖

事务表、回滚块、事务槽 三者之间的关系如下

3. ORA-01555错误
ORA_10555 "Snapshot too old" 是Oracle的一个经典错误。
原因:1)select语句执行时间太长;2)undo表空间太小,导致undo数据被覆盖;
解决办法:增加undo表空间大小,可以使用em中的undo advisor(指导中心-->>还原管理)
ORA_10555 "Snapshot too old" 

ORA-10555的两个案例:

阅读(2186) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~