Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1149024
  • 博文数量: 231
  • 博客积分: 2500
  • 博客等级: 少校
  • 技术积分: 2662
  • 用 户 组: 普通用户
  • 注册时间: 2009-11-03 16:35
个人简介

学无止境

文章分类

全部博文(231)

文章存档

2014年(7)

2013年(103)

2011年(11)

2010年(53)

2009年(57)

分类: Oracle

2010-02-08 14:08:16

TX锁最常见的有以下4种情况:
  1. 由于行被活动事务锁住导致的等待。
  2. 由于唯一约束或主键约束导致的等待。
  3. 由于数据块ITL槽不足导致的等待。
  4. 由于行包含在同一个位图索引片段中导致的等待。
在LDY用户下创建一张表,插入几条数据:
CREATE TABLE tx_eg (id number,name varchar2(10),sex varchar2(10)) INITRANS 1 MAXTRANS 1;
INSERT into tx_eg VALUES (1,'liaowh','MALE');
INSERT into tx_eg VALUES (2,'liaody','MALE');
INSERT into tx_eg VALUES (3,'wanghh','MALE');
INSERT into tx_eg VALUES (4,'zhangxx','FEMALE');
INSERT into tx_eg VALUES (5,'linjx','MALE');
COMMIT;
 
 
1.由于行被活动事务锁住导致的等待:
两个session同时update同一条记录。
Ses#1:  update tx_eg set name='yangkq' where id=5;
Ses#2: update tx_eg set name='yangkq' where id=5;
DBA用户查看:
select SID,TYPE,ID1,ID2,LMODE,REQUEST from v$lock where type='TX';
       SID TY        ID1        ID2      LMODE    REQUEST
---------- -- ---------- ---------- ---------- ----------
       144 TX     327692       8218          0          6
       146 TX     327692       8218          6          0
      
SELECT DECODE(request,0,'Holder: ','Waiter: ')||sid sess,
id1, id2, lmode, request, type
FROM V$LOCK
WHERE (id1, id2, type) IN
(SELECT id1, id2, type FROM V$LOCK WHERE request>0)
ORDER BY id1, request;
      
SESS                  ID1        ID2      LMODE    REQUEST TY
-------------- ---------- ---------- ---------- ---------- --
Holder: 146        327692       8218          6          0 TX
Waiter: 144        327692       8218          0          6 TX     

可以看出SID 144等待SID 146。SID 146持有1个排他锁(LMODE=6),而SID 144同样请求以排他形式获得该锁(REQUEST=6)。
由enqueue的概念可知,通过TYPE-ID1-ID2标识一个锁,所以这两个锁是同一个锁。
可以通过查询V$SESSION视图,找出具体对象的具体行。
通过以下四个字段:
 ROW_WAIT_OBJ#                                      NUMBER
 ROW_WAIT_FILE#                                     NUMBER
 ROW_WAIT_BLOCK#                                    NUMBER
 ROW_WAIT_ROW#                                      NUMBER
DBA用户查看:
select ROW_WAIT_OBJ#,
ROW_WAIT_FILE#,
ROW_WAIT_BLOCK#,
ROW_WAIT_ROW#
from v$session
where sid=144;
ROW_WAIT_OBJ# ROW_WAIT_FILE# ROW_WAIT_BLOCK# ROW_WAIT_ROW#
------------- -------------- --------------- -------------
        54398             14          102391             4
SID 144等待的对象号是54398,所在文件是FILE 14,等待的行所在的块号是102391,行号是4。
通过对象号,或是文件号+块号,都可以查找到该对象:
select owner,object_name,object_type from dba_objects where object_id=54398;
OWNER      OBJECT_NAME          OBJECT_TYPE
---------- -------------------- --------------------
LDY        TX_EG                TABLE

select segment_name, segment_type, owner
from dba_extents
where file_id = 14
and 102391 between block_id
and block_id + blocks -1;
SEGMENT_NAME     SEGMENT_TYPE       OWNER
---------------- ------------------ ----------
TX_EG            TABLE              LDY

而ROW_WAIT_ROW#表示ROWID中的row number,可以通过dbms_rowid.rowid_row_number(rowid)获得该行的row number。
可以查找到具体行的内容:
select * from ldy.tx_eg where DBMS_ROWID.ROWID_ROW_NUMBER(ROWID)=4;
        ID NAME       SEX
---------- ---------- ----------
         5 linjx      MALE

这时session2由于等待而被挂起,需要等待session commit、rollback或转变问为一个in-doubt状态,才能继续。
 

2.由于唯一约束或主键约束导致的等待:
如果一张表存在主键约束,唯一性约束或唯一索引,该列就会因为唯一索引的约束强制该列的唯一性。如果两个session需要插入同一个键值,那么第二个session将会等待或收到ORA-0001错误。
为tx_eg表增加一个主键约束:
alter table tx_eg add constraint tx_eg_pk primary key(id);
两个session同时insert具有相同ID的记录。
Ses#1:  insert into tx_eg values (10,'Xiongwh','MALE');
Ses#2: insert into tx_eg values (10,'Zengx',null);
DBA用户查看:
select SID,TYPE,ID1,ID2,LMODE,REQUEST from v$lock where type='TX';
       SID TY        ID1        ID2      LMODE    REQUEST
---------- -- ---------- ---------- ---------- ----------
       144 TX     393258       7967          0          4
       144 TX      65576       6264          6          0
       146 TX     393258       7967          6          0
      
SELECT DECODE(request,0,'Holder: ','Waiter: ')||sid sess,
id1, id2, lmode, request, type
FROM V$LOCK
WHERE (id1, id2, type) IN
(SELECT id1, id2, type FROM V$LOCK WHERE request>0)
ORDER BY id1, request;
      
SESS                 ID1        ID2      LMODE    REQUEST TY
------------- ---------- ---------- ---------- ---------- --
Holder: 146       393258       7967          6          0 TX
Waiter: 144       393258       7967          0          4 TX

可以看出SID 144等待SID 146。SID 146持有1个排他锁(LMODE=6),而SID 144请求以共享模式获得该锁(REQUEST=4)。
除此之外,SID 144自身还持有1个排他锁(LMODE=6)。
这时session2由于等待而被挂起,需要等待session commit、rollback或转变问为一个in-doubt状态,才能继续。
如果session1 commit,session2 则收到一个错误:ORA-00001: 违反唯一约束条件 (LDY.TX_EG_PK)。
如果session1 rollback,session2则可以顺利insert。
如果insert不同的ID记录或没有唯一性约束,则只有有2个TX排他锁,不存在阻塞。
       SID TY        ID1        ID2      LMODE    REQUEST
---------- -- ---------- ---------- ---------- ----------
       144 TX     458752       6034          6          0
       146 TX     589845       7585          6          0
      
 

3.由于数据块ITL槽不足导致的等待:
Oracle在每个数据块的顶部区域中的'interested transaction list'(ITL)保存被事务锁定的行。一个对象中的任何一个块的ITL槽的数量由参数INITRANS和参数MAXTRANS约束。INITRANS表示数据块在被创建时初始化的ITL数量。MAXTRANS表示ITL可以达到的最大数目。
MAXTRANS设置了在某一时间内,在数据块可以并发的事务数的上限。
INITRANS指定了数据块最小并发。

CREATE TABLE MT (id number(10),name varchar2(20)) INITRANS 1 MAXTRANS 1;
insert into MT values (1,'First');
insert into MT values (2,'Second');
insert into MT values (3,'Third');
 
SQL> select owner, INI_TRANS,max_TRANS from dba_tables where table_name='MT';
OWNER            INI_TRANS  MAX_TRANS
--------------- ---------- ----------
SCOTT                    1          1

Ses#1: update MT set name='Big' where ID=1;
Ses#2: update MT set name='Small' where ID=2;
Ses#3: update MT set name='Middle' where ID=3;
DBA用户查看:
select SID,TYPE,ID1,ID2,LMODE,REQUEST from v$lock where type='TX';
       SID TY        ID1        ID2      LMODE    REQUEST
---------- -- ---------- ---------- ---------- ----------
         9 TX     327716        328          6          0
        13 TX     393243        331          6          0
        14 TX     327716        328          0          4
SELECT DECODE(request,0,'Holder: ','Waiter: ')||sid sess,
id1, id2, lmode, request, type
FROM V$LOCK
WHERE (id1, id2, type) IN
(SELECT id1, id2, type FROM V$LOCK WHERE request>0)
ORDER BY id1, request;
SESS                         ID1        ID2      LMODE    REQUEST TY
--------------------- ---------- ---------- ---------- ---------- --
Holder: 9                 327716        328          6          0 TX
Waiter: 14                327716        328          0          4 TX

从dump文件上看,尽管设置了MAXTRANS为1,但是每个数据块都至少有2个ITL事务槽,所以需要有3个session时,第3个session会被hang住。
下面是dump的部分截取,可以看到2个事务槽都满了。
 Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01   0x0004.002.0000012b  0x00800437.0052.57  ----    1  fsc 0x0001.00000000
0x02   0x0001.029.000000bc  0x00800180.0036.01  ----    1  fsc 0x0002.00000000

从Oracle 9.2开始,可以通过v$segment_statistics视图检查ITL等待。
查询语句如下:
     set line 999
     col OWNER for a20
     col OBJECT_NAME for a30
     col STATISTIC_NAME for a10
     SELECT t.OWNER, t.OBJECT_NAME, t.OBJECT_TYPE, t.STATISTIC_NAME, t.VALUE
     FROM v$segment_statistics t
     WHERE t.STATISTIC_NAME = 'ITL waits'
     AND t.VALUE > 0
     ORDER BY VALUE DESC;
如有发现,可以适量增大INITTRANS和MAXTRANS。

关于ITL等待,需要之前的session1或session2处于决断状态(commit/rollback)之后才会统计ITL等待。
OWNER        OBJECT_NAME       OBJECT_TYPE        STATISTIC_     VALUE
------------ ----------------- ------------------ ---------- ---------
SCOTT        MT                TABLE              ITL waits          1

从10g开始表的MAXTRANS参数不能再设置,自动设置为255。从而不会导致ITL槽不足的而hang住的情况发生,但仍会等待分配ITL。
 
 
4.由于行包含在同一个位图索引片段中导致的等待:
位图索引的索引键值包含一个范围的ROWID,所以位图索引中的每个条目(entry)可能包含实际表中的许多行。如果两个session想要更新的行包含在同一个位图索引片段中,那么第二个session需要等待第一个session COMMIT或ROLLBACK,在此期间等待获得mode 4的TX锁。
在tx_eg表的sex字段上建立一个位图索引:
CREATE Bitmap Index tx_eg_bitmap on tx_eg ( sex );
2个session分别update 2条不同的记录,但是这2条记录的sex字段的内容都是MALE。
Ses#1:  MALE->FEMALE
Ses#2:  MALE->FEMALE
Ses#1:  update tx_eg set sex='FEMALE' where id=2;
Ses#2:  update tx_eg set sex='FEMALE' where id=3;
DBA用户查看:
select SID,TYPE,ID1,ID2,LMODE,REQUEST from v$lock where type='TX';
       SID TY        ID1        ID2      LMODE    REQUEST
---------- -- ---------- ---------- ---------- ----------
       144 TX     327719       8220          0          4
       144 TX     589864       7586          6          0
       146 TX     327719       8220          6          0
      
SELECT DECODE(request,0,'Holder: ','Waiter: ')||sid sess,
id1, id2, lmode, request, type
FROM V$LOCK
WHERE (id1, id2, type) IN
(SELECT id1, id2, type FROM V$LOCK WHERE request>0)
ORDER BY id1, request;
      
SESS                 ID1        ID2      LMODE    REQUEST TY
------------- ---------- ---------- ---------- ---------- --
Holder: 146       327719       8220          6          0 TX
Waiter: 144       327719       8220          0          4 TX

可以看出SID 144等待SID 146。SID 146持有1个排他锁(LMODE=6),而SID 144请求以共享模式获得该锁(REQUEST=4)。
除此之外,SID 144自身还持有1个排他锁(LMODE=6)。
这种情况很类似具有唯一索引的情况。
同样查看SID 144正在等待的对象:
DBA用户查看:
select ROW_WAIT_OBJ#,
ROW_WAIT_FILE#,
ROW_WAIT_BLOCK#,
ROW_WAIT_ROW#
from v$session
where sid=144;
ROW_WAIT_OBJ# ROW_WAIT_FILE# ROW_WAIT_BLOCK# ROW_WAIT_ROW#
------------- -------------- --------------- -------------
        54482              0               0             0
SID 144等待的对象号是54482,但没有其他信息,通过对象号查找到等待是该表上的位图索引:
select owner,object_name,object_type from dba_objects where object_id=54482;
OWNER      OBJECT_NAME          OBJECT_TYPE
---------- -------------------- --------------------
LDY        TX_EG_BITMAP         INDEX
考虑到是否位图索引只锁住一部分,于是增加做了以下4个实验:
SQL> select * from tx_eg;
        ID NAME       SEX
---------- ---------- ----------
        10 Xiongwh    MALE
         1 liaowh     MALE
         2 liaody     MALE
         3 wanghh     MALE
         4 zhangxx    FEMALE
         5 linjx      MALE
实验1:
Ses#1:  FEMALE->MALE
Ses#2:  MALE->FEMALE
Ses#1:  update tx_eg set sex='MALE' where id=4;
Ses#2:  update tx_eg set sex='FEMALE' where id=3;
实验1结果:需要等待。
实验2:
Ses#1:  MALE->MALE
Ses#2:  MALE->FEMALE
Ses#1:  update tx_eg set sex='MALE' where id=2;
Ses#2:  update tx_eg set sex='FEMALE' where id=3;
实验2结果:不会等待,2个session都可以update。
实验3:
Ses#1:  MALE->FEMALE
Ses#2:  MALE->MALE
Ses#1:  update tx_eg set sex='FEMALE' where id=2;
Ses#2:  update tx_eg set sex='MALE' where id=3;
实验3结果:不会等待,2个session都可以update。
实验4:
Ses#1:  MALE->FEMALE
Ses#2:  FEMALE->FEMALE
Ses#1:  update tx_eg set sex='FEMALE' where id=3;
Ses#2:  update tx_eg set sex='FEMALE' where id=4;
实验4结果:不会等待,2个session都可以update。
实验结论:只要另一个session需要修改位图索引,则将位图索引的一个位图段锁住,另一个session想要修改位图索引的相同位图段,就必须等待。
阅读(1436) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~