oracle 锁的引用,Oracle中的锁(LOCK)机制详解

ITL 引起的阻塞

当block中没有多余的空间来添加ITL entry的时候,就会发生阻塞。具体可以看下面的例子:

_dexter@FAKE>create table tb_itl (id int , name varchar2(4000)) pctfree 0 initrans 1 ;

Tablecreated.

_dexter@FAKE>insert into tb_itl select level , 'd' from dual connect by level <= 10000 ;

10000 rowscreated.

_dexter@FAKE>commit ;

Commitcomplete.

_dexter@FAKE>update tb_itl set name=lpad('x',2000,name) ;

10000 rowsupdated.

_dexter@FAKE>commit ;

Commitcomplete.

上面的操作保证至少第一个block中不会有多余的空间

selectt.id,

dbms_rowid.rowid_relative_fno(t.rowid)as "FNO#",

dbms_rowid.rowid_block_number(t.rowid)as "BLK#",

dbms_rowid.rowid_row_number(t.rowid) as"ROW#"

from dexter.tb_itl t

whererownum<5 ;

_dexter@FAKE>select t.id,

2         dbms_rowid.rowid_relative_fno(t.rowid) as "FNO#",

3         dbms_rowid.rowid_block_number(t.rowid) as "BLK#",

4         dbms_rowid.rowid_row_number(t.rowid) as "ROW#"

5    from dexter.tb_itl t

6 where id<5 ;

ID      FNO#      BLK#      ROW#

-------------------- ---------- ----------

1          6        187          0

2          6        187          1

3          6        187          2

4          6        187          3

先dump一下看一下block中剩余有几个itl slot

Itl          Xid                  Uba        Flag Lck        Scn/Fsc

0x01  0x0006.016.00000a60  0x00c000ef.0284.14  C---   0  scn 0x0000.003d7a84

0x02  0x0003.01c.000009ea  0x00c00153.028c.1c  ---- 733  fsc 0x0000.00000000

只有2个事务槽了。

下面内容引用自网络。

每个ITL entry包括以下的内容:

Transactionid(Xid): 8bytes。其中包括rollback segment number, transaction table中的slot number等。

Undoblock address(Uba): 8bytes。其中包括rollback segment block的DBA,sequence number等。

Flags:1nibble。

---- =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

--U- =transaction committed (maybe long ago); SCN is an upper bound

---T =transaction was still active at block cleanout SCN

Locks:3nibbles. 也就是所谓的行级锁(row-level locks)

SCN orfree space credit: 6bytes. 如果这个事务已经clean out,这个值就是SCN;否则,前两个字节表示由这个事务释放的此block中的空间数。

我们来尝试更改一下数据

session1 session_id=144:

_dexter@FAKE>update tb_itl set name=lpad('x',2000,name) where id =1 ;

1 rowupdated.

session2 session_id=18:

_dexter@FAKE>  update tb_itl set name=lpad('x',2000,name)where id =2 ;

1 rowupdated.

session3 session_id=21:

_dexter@FAKE>update tb_itl set name=lpad('x',2000,name) where id =3 ;

waiting...

看一下锁信息

_sys@FAKE>select sid , type , id1 , lmode , request , block

2   from v$lock l

3  where sid in (select session_id from v$locked_object)

4    and type in ('TM', 'TX')

5  order by 1 ;

SID TYPE        ID1     LMODE    REQUEST      BLOCK

-------------- ---------- ---------- ---------- ----------

18 TX      393241          6          0          1

18 TM        82846          3          0          0

21 TX      393241          0          4          0

21 TM        82846          3          0         0

144 TX      131088          6          0          0

144 TM        82846          3          0          0

6 rowsselected.

_sys@FAKE>select sid,seq#,event from v$session_wait where sid= 21 ;

SID      SEQ# EVENT

---------- ---------------------------------------------------------

21        268 enq: TX - allocate ITL entry

_sys@FAKE>

因为在block 187中无法添加更多的ITL entry(拓展一个只需要24b)而引发的阻塞。

Session1

Session2

Session3

Description

T1

update tb_itl set name=lpad('x',2000,name) where id =1 ;

T2

update tb_itl set name=lpad('x',2000,name) where id =2 ;

T3

update tb_itl set name=lpad('x',2000,name) where id =3 ;

waiting…

常理来说这里应该顺利执行才对,可是事实上,因为block中无法再拓展出ITL entry,所以它被阻塞。

通常情况下不会发生这种情况。

解决办法:设置表的inittrans 参数为合理值。

Bitmap 引起的阻塞

_dexter@FAKE>create table tb_bitmap_test (id number , gender varchar2(1)) ;

Tablecreated.

_dexter@FAKE>insert into tb_bitmap_test select level , 'F'from dual connect by level <= 3;

3 rowscreated.

_dexter@FAKE>insert into tb_bitmap_test select level , 'M'from dual connect by level <= 2;

2 rowscreated.

_dexter@FAKE>create bitmap index tb_bitmap_test_btidx1 on tb_bitmap_test(gender) ;

Indexcreated.

_dexter@FAKE>select * from tb_bitmap_test ;

ID GE

------------

1 F

2 F

3 F

1 M

2 M

session1 session_id=144:

_dexter@FAKE>update tb_bitmap_test set gender='M' where id=1 and gender='F' ;

1 rowupdated.

session2 session_id=18:

_dexter@FAKE>delete tb_bitmap_test where gender='M' and id = 1;

waiting...

session3 session_id=9 :

_dexter@FAKE>insert into tb_bitmap_test values (1,'S') ;

1 rowcreated.

锁情况:

_sys@FAKE>@lock

SID TYPE        ID1     LMODE    REQUEST      BLOCK

-------------- ---------- ---------- ---------- ----------

9 TM        82847          3          0          0

9 TX      196626          6          0         0

18 TX      327710          6          0          0

18 TM        82847          3          0          0

18 TX      589854          0          4          0

144 TX      589854          6          0          1

144 TM        82847          3          0          0

7 rowsselected.

不管是gender='M' 或者 'F' ,只要涉及到这两个字段的值的dml操作都将进入等待当中(包括insert)

因为第一个session锁住了整个bitmap segment。但是只要gender的值不涉及M或者F即可顺利执行。所以session3顺利的执行。

Session1

Session2

Session3

Description

T1

update tb_bitmap_test set gender='M' where id=1 and gender='F' ;

因为有了Bitmap索引,所以这个操作会索引表中所有gender=’M’和‘F’的记录,并且会阻塞相关的insert操作

T2

delete tb_bitmap_test where gender='M' and id = 1;

waiting…

这里发生了阻塞

T3

insert into tb_bitmap_test values (1,'S') ;

只要gender的值不等于M或者F即可顺利执行

2.模拟RI锁定导致阻塞的场景。

初始化环境

_dexter@FAKE>create table tun2_p (id int primary key) ;

Tablecreated.

_dexter@FAKE>create table tun2_c (pid references tun2_p(id)) ;

Tablecreated.

_dexter@FAKE>insert into tun2_c values (1) ;

insert intotun2_c values (1)

*

ERROR atline 1:

ORA-02291:integrity constraint (DEXTER.SYS_C0014143) violated - parent key not found

这里因为有引用完整性约束,子表中的内容必须与父表中的内容匹配。因为父表中没有id=1的记录,所以这里报错

主表插入

_dexter@FAKE>insert into tun2_p values (2) ;

1 rowcreated.

lock status:

_sys@FAKE>@lock2

OBJECT_NAME                          SID TYPE        ID1     LMODE    REQUEST      BLOCK

---------------------------------------- ---- ---------- ---------- ---------- ----------

TUN2_P                                  9 TM        82949          3          0          0

9TX      589829          6          0         0

TUN2_C                                  9 TM        82952          3          0          0

主表更新(子表中没有引用的记录)

_dexter@FAKE>update tun2_p set id=3 where id=2 ;

1 rowupdated.

lock status:

_sys@FAKE>@lock2

OBJECT_NAME                          SID TYPE        ID1     LMODE    REQUEST      BLOCK

---------------------------------------- ---- ---------- ---------- ---------- ----------

9TX      262144          6          0          0

TUN2_P                                  9 TM        82949          3          0          0

主表删除(子表中没有引用的记录)

_dexter@FAKE>delete tun2_p where id=3 ;

1 rowdeleted.

lock status:

_sys@FAKE>@lock2

OBJECT_NAME                          SID TYPE        ID1     LMODE    REQUEST      BLOCK

---------------------------------------- ---- ---------- ---------- ---------- ----------

9TX      524294          6          0          0

TUN2_P                                  9 TM        82949          3          0          0

如果upadte和delete操作中不包含子表引用的记录,就不会对子表加锁。

而insert相对比较复杂一点,它会级联的将子表锁定。

如果在子表引用的记录上发生更改,则会报错。例如:

_dexter@FAKE>update tun2_p set id=3 where id=1 ;

updatetun2_p set id=3 where id=1

*

ERROR atline 1:

ORA-02292:integrity constraint (DEXTER.SYS_C0014143) violated - child record found

子表插入

_dexter@FAKE>insert into tun2_c values (2) ;

1 rowcreated.

lock:

_sys@FAKE>@lock2

OBJECT_NAME                          SID TYPE        ID1     LMODE    REQUEST      BLOCK

---------------------------------------- ---- ---------- ---------- ---------- ----------

TUN2_P                                  9 TM        82949          3          0          0

9TX      589825          6          0          0

TUN2_C                                 9 TM        82952          3          0          0

子表更新

_dexter@FAKE>update tun2_c set pid=1 where pid=2 ;

1 rowupdated.

lock

_sys@FAKE>@lock2

OBJECT_NAME                          SID TYPE        ID1     LMODE    REQUEST      BLOCK

---------------------------------------- ---- ---------- ---------- ---------- ----------

TUN2_P                                  9 TM        82949          3          0          0

9TX      655390          6          0          0

TUN2_C                                  9 TM        82952         3          0          0

子表删除

_dexter@FAKE>delete from tun2_c where pid=1 ;

2 rowsdeleted.

lock:

_sys@FAKE>@lock2

OBJECT_NAME                          SID TYPE        ID1     LMODE    REQUEST      BLOCK

---------------------------------------- ---- ---------- ---------- ---------- ----------

TUN2_P                                  9 TM        82949          3          0          0

9TX      196635          6          0          0

TUN2_C                                  9 TM        82952          3          0          0

子表的记录一定会引用到父表的记录,所以在对子表进行dml操作的时候,都会锁定父表。

复杂示例

两个表中现在么有任何记录。

session1 session_id=9:

_dexter@FAKE>insert into tun2_p values (1) ;

1 rowcreated.

看一下锁的情况:

_sys@FAKE>@lock2

OBJECT_NAME                           SID TYPE        ID1     LMODE    REQUEST      BLOCK

---------------------------------------- ---- ---------- ---------- ---------- ----------

TUN2_C                                  9 TM        82952          3          0          0

TUN2_P                                  9 TM        82949          3          0          0

9TX      262149          6          0          0

可以看到,当向父表中插入记录的时候,会同时锁定父表和子表,加表的3级共享锁。

session1没提交之前其他事���无法看到父表中的id=1的记录,我们再来尝试一下向子表中插入pid=1的记录

session2 session_id=18:

_dexter@FAKE>insert into tun2_c values (1) ;

waiting ...

可以看到session2 进入了阻塞状态,我们来查看一下锁的情况

_sys@FAKE>@lock2

OBJECT_NAME                          SID TYPE        ID1     LMODE    REQUEST      BLOCK

---------------------------------------- ---- ---------- ---------- ---------- ----------

TUN2_C                                  9 TM        82952          3          0          0

9TX      262149          6          0          1

TUN2_P                                  9 TM        82949          3          0          0

18TX      262149          0          4          0

TUN2_C                                18 TM        82952          3          0          0

18TX      589848          6          0          0

TUN2_P                                18 TM        82949          3          0          0

7 rowsselected.

首先我们可以看到,session2也有两个TM表锁,分别锁定了子表和父表。这说明在子表更新数据的时候,也会对引用的对象加锁。

然后我们还看到,子表陷入了等待当中。

这是因为session2 中的事务是否能够成功执行,取决于session1 中的事务状态。而session1 中的事务现在是悬而未决的状态。

是不是有点和读一致性搞混了?觉得第二个session中的事务不应该进入阻塞当中,而是直接报错?

它不像读一致性,可以在查询的时候根据undo获取一个一致性视图。

在事务执行的时候,只和数据的当前状态相关。

第一个session的事务rollback后

session2就会报错

_dexter@FAKE>insert into tun2_c values (1) ;

insert intotun2_c values (1)

*

ERROR atline 1:

ORA-02291:integrity constraint (DEXTER.SYS_C0014143) violated - parent key not found

Session1

Session2

Description

T1

insert into tun2_c values (1) ;

T2

insert into tun2_c values (1) ;

waiting…

正常理解,这里应该直接报错,ORA-02291才对,但是这里没有,因为父表中id=1的记录还是悬而未决的状态。这是智能呢?还是智能呢?还是智能呢?那就是智能吧。

T3

rollback

T4

Raise error

ORA-02291: integrity constraint (DEXTER.SYS_C0014143) violated - parent key not found

一切都明了了,报错了。

3.从mode 2-6 的TM锁相互间的互斥示例。

再次引用这张表

RS|SS

RX|SX

S

SRX|SSX

X

RS|SS

×

RX|SX

×

×

×

S

×

×

×

SRX|SSX

×

×

×

×

X

×

×

×

×

×

介绍一些操作

lock table tun2_tab in ROW SHARE mode ;

lmode=2

lock table tun2_tab in ROW EXCLUSIVE mode ;

lmode=3

lock table tun2_tab in SHARE MODE ;

lmode=4

lock table tun2_tab in SHARE ROW EXCLUSIVE MODE ;

lmode=5

lock table tun2_tab in EXCLUSIVE MODE ;

lmode=6

下面的示例演示验证上表的内容

Row Share (RS)

Also called a subshare table lock (SS)

Session1 session_id=35:

dexter@STARTREK>create table tun2_tab (x int) ;

Tablecreated.

dexter@STARTREK>lock table tun2_tab in ROW SHARE mode nowait ;

Table(s)Locked.

session2 session_id=160:

dexter@STARTREK>lock table tun2_tab in ROW SHARE mode ;

Table(s)Locked.

dexter@STARTREK>commit ;

Commitcomplete.

dexter@STARTREK>lock table tun2_tab in ROW EXCLUSIVE mode ;

Table(s)Locked.

dexter@STARTREK>commit ;

Commitcomplete.

dexter@STARTREK>lock table tun2_tab in SHARE MODE ;

Table(s)Locked.

dexter@STARTREK>commit ;

Commitcomplete.

dexter@STARTREK>lock table tun2_tab in SHARE ROW EXCLUSIVE MODE ;

Table(s)Locked.

dexter@STARTREK>commit;

Commitcomplete.

dexter@STARTREK>lock table tun2_tab in EXCLUSIVE MODE ;

waiting ...

看一下锁的情况

sys@STARTREK>@lock

SID TY        ID1     LMODE    REQUEST      BLOCK

------------ ---------- ---------- ---------- ----------

35 TM      76917          2          0          1

160 TM      76917          0          6          0

RS|SS锁和X锁是不能并发的,但是可以兼容其他类型的锁。

Row Exclusive TableLock (RX|SX)

Also called a subexclusive table lock (SX)

Session1 session_id=35:

dexter@STARTREK>lock table tun2_tab in ROW EXCLUSIVE mode ;

Table(s)Locked.

Session2 session_id=160:

dexter@STARTREK>lock table tun2_tab in ROW SHARE mode ;

Table(s)Locked.

dexter@STARTREK>commit ;

Commitcomplete.

dexter@STARTREK>lock table tun2_tab in ROW EXCLUSIVE mode ;

Table(s)Locked.

dexter@STARTREK>commit ;

Commitcomplete.

dexter@STARTREK>lock table tun2_tab in SHARE MODE ;

waiting ...

看一下锁的情况

sys@STARTREK>/

SID TY        ID1     LMODE    REQUEST      BLOCK

------------ ---------- ---------- ---------- ----------

35 TM      76917          3          0          1

160 TM      76917          0          4          0

RX|SX与S锁是无法并发的,经测试SRX|SSX锁也一样无法与RX|SX锁并发。0b1331709591d260c1c78e86d0c51c18.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ORACLE有以下几种模式: 0:none 1:null 空 2:Row-S 行共享(RS):共享表 3:Row-X 行专用(RX):用于行的修改 4:Share 共享(S):阻止其他DML操作 5:S/Row-X 共享行专用(SRX):阻止其他事务操作 6:exclusive 专用(X):独立访问使用 数字越大级别越高, 影响的操作越多。 一般的查询语句如select ... from ... ;是小于2的, 有会在v$locked_object出现。 select ... from ... for update; 是2的。 当对话使用for update子串打开一个游标, 所有返回集的数据行都将处于行级(Row-X)独占式定, 其他对象只能查询这些数据行,不能进行update、delete或select...for update操作。 insert / update / delete ... ; 是3的。 没有commit之前插入同样的一条记录会没有反应, 因为后一个3的会一直等待上一个3的, 我们必须释放掉上一个才能继续工作。 创建索引的候也会产生3,4级别的locked_mode为2,3,4不影响DML(insert,delete,update,select)操作, 但DDL(alter,drop等)操作会提示ora-00054错误。 有主外键约束 update / delete ... ; 可能会产生4,5的。 DDL语句是6的。 以DBA角色, 查看当前数据库里的情况可以用如下SQL语句: select object_id,session_id,locked_mode from v$locked_object; select t2.username,t2.sid,t2.serial#,t2.logon_time from v$locked_object t1,v$session t2 where t1.session_id=t2.sid order by t2.logon_time; 如果有长期出现的一列,可能是没有释放的。 我们可以用下面SQL语句杀掉长期没有释放非正常的: alter system kill session 'sid,serial#'; 如果出现了的问题, 某个DML操作可能等待很久没有反应。 当你采用的是直接连接数据库的方式, 也不要用OS系统命令 $kill process_num 或者 $kill -9 process_num来终止用户连接, 因为一个用户进程可能产生一个以上的, 杀OS进程并不能彻底清除的问题。 记得在数据库级别用alter system kill session 'sid,serial#';杀掉不正常的
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值