oracle锁机制研究

引言:Oracle 锁在我一开始接触的时候会有一种高深莫测的感觉,就像是遥远的外星人看不见摸不着但是能感觉到,我在实际的工作中就遇到过ORA-00054: resource busy acquire with nowait specified错误不能插入表,当时知道是被锁定了,根据V$LOCK也定位出阻塞的会话了,但不知道如何长久的解决它,究其原因就是不清楚内部机制与释放原理,下面根据例子来揭开锁的面纱,走进Oracle锁的世界。

1.分别模拟insert,update和delete造成阻塞的示例,并对v$lock中的相应的信息进行说明,给出SQL演示。

LEO1@LEO1> create user leo2 identified by leo2 default tablespace leo1;       新创建一个LEO2用户
User created.
LEO1@LEO1> grant connect,resource to leo2;                                             授予基本权限
Grant succeeded.
LEO1@LEO1> select owner,table_name,tablespace_name from dba_tables where owner='LEO1';
OWNER                          TABLE_NAME                     TABLESPACE_NAME
------------------------------ ------------------------------ ------------------------------
LEO1                               LEO_WAGE                       LEO1

LEO1@LEO1> create table t1 (id int primary key);     创建t1表,设置id列为主键
Table created.
LEO1@LEO1> insert into t1 values(1);                  
1 row created.
LEO1@LEO1> select * from t1;
        ID
----------
         1
LEO2@LEO1> insert into leo1.t1 values(1);             当没有提交,在插入同样的values时就发生了对会话的阻塞,hang在这里不能前进
LEO1@LEO1> commit;                                          必须提交后,阻塞才终止,也就是说commit可以释放阻塞
Commit complete.
LEO2@LEO1> insert into leo1.t1 values(1);             因为已经有了1值,故违反了主键约束
insert into leo1.t1 values(1)
*
ERROR at line 1:
ORA-00001: unique constraint (LEO1.SYS_C0010817) violated

update 锁阻塞

LEO1@LEO1> select * from t1;
        ID
----------
         1
         2
       100
LEO1@LEO1> update t1 set id=200 where id=100;       更新一行没有提交,没有提交的事物对别人是不可见的,但在物理块上真真切切的修改了,他人只能访问undo回滚段中镜像
1 row updated.
LEO2@LEO1> update leo1.t1 set id=300 where id=100;   我们在会话leo2上也更新同一个表里的同一行,此时hang住了不动了,因为2个会话在争用同一条记录的修改权
LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TM','TX') order by 1,2;        一般影响业务性能的就 TM and TX 锁
       SID TY        ID1        ID2      LMODE    REQUEST      BLOCK
---------- -- ---------- ---------- ---------- ---------- ----------
       138 TM      73449          0          3          0          0
       138 TX     196615      912          6          0          1
       156 TM      73449          0          3          0          0
       156 TX     196615      912          0          6          0

SID:会话id号
TYPE:锁的类型
ID1:会话操作对象的id号
ID2:ID1+ID2 定位回滚段上的一个地址(即修改之前数据镜像地址),由于138和156会话是一样的说明指向的是同一个地址,换句话说操作的是同一行数据
LMODE:锁模式,不同的数字代表不同的锁模式  例如  0 现在没有申请到锁   3  共享锁模式(段级共享锁)   6  排他锁模式   锁的级别越高限制越多
REQUEST:目前会话没有锁,正在申请的锁模式  例如  0 没有正在申请的锁,说明已经有锁了   6  现在正在申请6号锁,目前因为没有才申请
BLOCK:当前正在阻塞几个会话  例如  1  当前正在阻塞一个会话  2  当前正在阻塞两个会话
锁的实质:是维护一个事务完整性的,锁的信息是数据块的一个属性,是物理的,并不是逻辑上属于某个表或者某几行的。

LEO1@LEO1> select distinct sid from v$mystat;        这个会话当前id是138,我们怎么区分呢?=> LEO1用户=138     LEO2用户=156
       SID
----------
       138
LEO1@LEO1> select object_name from dba_objects where object_id=73449;                     138会话操作的对象是T1表
OBJECT_NAME
--------------------------------------------------------------------------------
T1
说明:138会话在T表上加了TM和TX锁,TM锁模式为3(共享锁) TX锁模式6(排他锁),目前TX锁正在阻塞一个会话(就是156会话)。
156会话就是当前被阻塞的会话,156会话操作对象也是T1表(ID1都一样的),TM锁模式也为3(共享锁就是有几个会话就可以创建几个共享锁,同时存在),TX现在还没有申请到锁,正在申请6号锁,而这个6号锁就是138会话所持有的(因为2个会话操作的是同一行数据)
LEO1@LEO1> select sid,event from v$session_wait where sid in (138,156);         从会话等待视图上可以看出,有哪些会话由于什么原因导致等待事件不能前进
       SID EVENT
---------- ----------------------------------------------------------------
       138 SQL*Net message to client
       156 enq: TX - row lock contention
156会话由于TX锁争用原因导致hang住不能前进,enq=enqueues队列锁(通常和业务有关,为了保护业务的锁)
小结:现在我们应该很晴朗的看出138会话阻塞156会话,以及阻塞的原因和会话数和锁类型

insert 锁阻塞

LEO1@LEO1> select * from leo1.t1;
        ID
----------
         1
         2
       200
LEO1@LEO1> insert into leo1.t1 values(3);       插入一行但没有提交,这是一个未决状态,还不清楚是否真正插入
1 row created.
LEO2@LEO1> insert into leo1.t1 values(3);       我们在会话leo2上也插入同样的数据,此时hang住了不动了,这里实际上是插入了2条独立的记录,不能认为是同一条记录,只是值一样
LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TM','TX') order by 1,2;
       SID TY        ID1        ID2      LMODE    REQUEST      BLOCK
---------- -- ---------- ---------- ---------- ---------- ----------
       138 TM      73449          0          3          0          0
       138 TX      65555        681          6          0          1        138正在阻塞另一个会话
       156 TM      73449          0          3          0          0
       156 TX     458766       669          6          0          0        这也显示了在插入第二条记录时没有被阻塞
       156 TX      65555        681          0          4          0        而在插入的修改值相同后被阻塞了,锁的级别是4
insert时v$lock视图里面多了一个TX锁(就是最后一行),首先说明一下insert和update delete 操作的不同,后两者都是对同一条记录的修改权争用产生阻塞(这里不涉及修改值的问题),而insert操作实际上插入了2条不同的记录,由于这2条不同的记录的修改值一样违反了主键约束从而产生阻塞,实际是对修改值的相同产生了阻塞。锁的级别为4,这种锁比update的锁级别要低,锁的级别越低限制越少。

delete 锁阻塞

LEO1@LEO1> select * from leo1.t1;                  t1表中有5条记录,我们计划删除的是最后1条
        ID
----------
         1
         2
         4
         5
       200
LEO1@LEO1> delete from leo1.t1 where id=200;       138会话正在删除id=200的记录,但是没有提交,此时就是加上一个TM TX锁
1 row deleted.
LEO2@LEO1> delete from leo1.t1 where id=200;       这时158会话也做同样的动作,就被hang住了不能动了,下面我们来看看锁定情况
LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TM','TX') order by 1,2;
       SID TY        ID1        ID2      LMODE    REQUEST      BLOCK
---------- -- ---------- ---------- ---------- ---------- ----------
       138 TM      73449          0          3          0          0
       138 TX     524316      935          6          0          1               138会话持有一个6级排他锁,正在阻塞一个会话
       156 TM      73449          0          3          0          0
       156 TX     524316      935          0          6          0               156会话被阻塞住了没有获得锁,正在申请一个6级锁
LEO1@LEO1> select object_name from dba_objects where object_id=73449;              现在可知锁定的就是t1表
OBJECT_NAME
--------------------------------------------------------------------------------
T1
LEO1@LEO1> select sid,event from v$session_wait where sid in (138,156);            从会话等待视图上也可以看出138阻塞了156会话
       SID EVENT
---------- ----------------------------------------------------------------
       138 SQL*Net message from client
       156 enq: TX - row lock contention
156会话由于TX锁争用原因导致hang住不能前进,enq=enqueues队列锁
小结:我们在了解锁的同时,也要在业务设计的流程上尽量去避免它们的发生,比如说2个人的工作没有协调好,在同一时间去做了同一件事,这就有可能产生锁。

select...for update 锁阻塞                                     这是一种对结果集修改的保护机制

场景:一次性修改多条记录的时候会用到这个命令,起到锁定结果集的效果,这也是结果集修改引起的阻塞
LEO1@LEO1> select * from leo1.t1;      
        ID
----------
         1
         2
         3
       200
LEO1@LEO1> select * from leo1.t1 where id<=3 for update;        如果我们想对查询出的结果集进行独占,并且此时不允许其他会话进行修改,可以这么来写
        ID
----------
         1
         2
         3
LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TM','TX') order by 1,2;
       SID TYPE              ID1        ID2      LMODE    REQUEST      BLOCK
---------- ---------- ---------- ---------- ---------- ---------- ----------
       138 TM              73449          0          3          0          0
       138 TX             589839      915          6          0          0           这3行记录都已经被TX锁锁定了,在没有提交之前别人不能修改
LEO2@LEO1> update leo1.t1 set id=4 where id=1;
LEO2@LEO1> update leo1.t1 set id=4 where id=2;
LEO2@LEO1> update leo1.t1 set id=4 where id=3;                   我们在会话leo2上测试更新结果集中的每条记录,都会hang住了不能前进,说明这
个结果集已经整体被锁定
LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TM','TX') order by 1,2;
       SID TY        ID1        ID2      LMODE    REQUEST      BLOCK
---------- -- ---------- ---------- ---------- ---------- ----------
       138 TM      73449          0          3          0          0
       138 TX     589839      915          6          0          1      138正在阻塞另一个会话
       156 TM      73449          0          3          0          0
       156 TX     589839      915          0          6          0

LEO1=138会话   LEO2=156会话,我们可以看出138会话阻塞156会话,156会话TX在请求一个6号排他锁,因为2个会话都在修改同一个结果集。这种方法可以一次性锁定n行记录。
重点:一个表上只能有一个6号锁

2.模拟RI锁定导致阻塞的场景,并分析v$lock相应的锁定信息,给出SQL演示。

LEO1@LEO1> create table a (id int primary key);                     a是主表,定义了id字段为主键
Table created.
LEO1@LEO1> create table b (id references a(id));                    b是从表,id字段是引用主表的id字段
Table created.
LEO1@LEO1> insert into a values(1);                                      往主表a中插入一条数据但没有提交,事务没有结束会产生锁定
1 row created.
LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TM','TX') order by 1,2;
       SID TYPE              ID1        ID2      LMODE    REQUEST      BLOCK
---------- ---------- ---------- ---------- ---------- ---------- ----------
       138 TM              73465          0          3          0          0     insert由于有从属关系因此会在2个表上都加3号共享锁
       138 TM              73467          0          3          0          0     
       138 TX             196640      940          6          0          0
LEO1@LEO1> select object_name from dba_objects where object_id in (73465,73467);  ID1就是138会话操作的对象id,我们会在主表和从表上都加上表级锁
OBJECT_NAME
--------------------------------------------------------------------------------
A           73465
B           73467
LEO1@LEO1> commit;                                                   提交之后释放锁
Commit complete.
LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TM','TX') order by 1,2;    锁会随着事务的结束而释放
no rows selected
LEO1@LEO1> select * from a;                                        a表中有一条记录
        ID
----------
         1
LEO1@LEO1> select * from b;
no rows selected
LEO1@LEO1> update a set id=100 where id=1;                           主表a上更新了一条记录
1 row updated.
LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TM','TX') order by 1,2;
       SID TYPE              ID1        ID2      LMODE    REQUEST      BLOCK
---------- ---------- ---------- ---------- ---------- ---------- ----------
       138 TM              73465          0          3          0          0          update现在只对主表有锁定,从表没有锁定
       138 TX             196634      941          6          0          0
LEO1@LEO1> commit;                                                   提交之后释放锁
Commit complete.
LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TM','TX') order by 1,2;      现在没有锁了
no rows selected
LEO1@LEO1> select * from a;          主表里的值已经更新了
        ID
----------
       100
LEO1@LEO1> delete from a;            
1 row deleted.
LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TM','TX') order by 1,2;
       SID TYPE              ID1        ID2      LMODE    REQUEST      BLOCK
---------- ---------- ---------- ---------- ---------- ---------- ----------
       138 TM              73465          0          3          0          0         delete也是只对主表有锁定,从表没有锁定
       138 TX             655375      705          6          0          0
LEO1@LEO1> commit;                                                               提交释放锁
Commit complete.
LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TM','TX') order by 1,2;
no rows selected

LEO1@LEO1> insert into b values(2);                                   
insert into b values(2)
*
ERROR at line 1:
ORA-02291: integrity constraint (LEO1.SYS_C0010831) violated - parent key not found   直接给从表插入记录,如果主表没有的话,会报错违反引用完整性约束,没有主表依据

LEO1@LEO1> insert into a values(2);      我们只能先给主表插入
1 row created.
LEO1@LEO1> insert into b values(2);      再给从表插入才可以,因为从表的数据必须在主表里先存在,才能正常引用
1 row created.
LEO1@LEO1> select * from a;              主表有了
        ID
----------
         2
LEO1@LEO1> select * from b;              从表有了
        ID
----------
         2
LEO1@LEO1> select * from a;              主表有3
        ID
----------
         2
         3
LEO1@LEO1> insert into b values(3);   才能给从表插入
1 row created.
LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TM','TX') order by 1,2;
       SID TYPE              ID1        ID2      LMODE    REQUEST      BLOCK
---------- ---------- ---------- ---------- ---------- ---------- ----------
       138 TM              73465          0          3          0          0    从表的insert也会对主从表同时加锁
       138 TM              73467          0          3          0          0
       138 TX             393246      939          6          0          0
LEO1@LEO1> select * from b;
        ID
----------
         2
         3
LEO1@LEO1> commit;                释放锁
Commit complete.
LEO1@LEO1> delete from b;       删除从表
2 rows deleted.

LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TM','TX') order by 1,2;
       SID TYPE              ID1        ID2      LMODE    REQUEST      BLOCK
---------- ---------- ---------- ---------- ---------- ---------- ----------
       138 TM              73465          0          3          0          0    从表delete也会对主从表同时加锁
       138 TM              73467          0          3          0          0
       138 TX             196620      944          6          0          0

LEO1@LEO1> commit;                                  释放锁
Commit complete.
LEO1@LEO1> select * from a;                      主表有2条记录
        ID
----------
         2
         3
LEO1@LEO1> select * from b;                      从表没有记录
no rows selected

LEO1@LEO1> insert into a values(4);            向主表插入1条记录,因为没有提交所以是未决状态
1 row created.
LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TM','TX') order by 1,2;
       SID TYPE              ID1        ID2      LMODE    REQUEST      BLOCK
---------- ---------- ---------- ---------- ---------- ---------- ----------
       138 TM              73465          0          3          0          0      此时对主从表都加了锁定
       138 TM              73467          0          3          0          0
       138 TX             589834      937          6          0          0
LEO2@LEO1> insert into leo1.b values(4);          此时向从表也插入1条记录,由于从表的数据必须引用自主表,而主表数据现在是一种未决状态,所以hang住不能前进
LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TM','TX') order by 1,2;
       SID TYPE              ID1        ID2      LMODE    REQUEST      BLOCK
---------- ---------- ---------- ---------- ---------- ---------- ----------
       138 TM              73465          0          3          0          0
       138 TM              73467          0          3          0          0
       138 TX             589834      937          6          0          1      138会话阻塞156会话,这个6代表已经插入成功,但修改值还是未决状态
       156 TM              73467          0          3          0          0
       156 TM              73465          0          3          0          0
       156 TX             393221      942          6          0          0      这个6代表也已经插入成功,但修改值还是未决状态
       156 TX             589834      937          0          4          0      主从表插入后产生了2个TX锁,这说明这是2条不同的记录,2个独立的记录,不是争用同一条记录

小结:之所以还有一个TX锁正在申请4号锁,是因为2条记录的修改值都是未决状态违反了引用完整性约束从而产生阻塞。导致156会话hang住不能前进。

3.自己构想一个使用手工锁定解决一种业务需求的场景,并给出SQL演示。

场景:手工锁定一个表,例如 我们在做一个秘密交易的时候,谈好的价格就不能变了,必须一手交钱一手交货完成交易,为了保证价格安全,我们先把价格表锁定,这期间不允许篡改,保证完成交易
LEO1@LEO1> lock table t1 in share mode;                                    手工锁定一个表,设置锁的级别为4
Table(s) Locked.
LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TM','TX') order by 1,2;
       SID TYPE              ID1        ID2      LMODE    REQUEST      BLOCK
---------- ---------- ---------- ---------- ---------- ---------- ----------
       138 TM              73449          0          4          0          0             已经在T1表上加上了4号锁,除了当前会话外阻塞其他会话进行修改
LEO2@LEO1> insert into leo1.t1 values(5);                                   我们在会话leo2上插入一条记录,此时hang住了不动了
LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TM','TX') order by 1,2;
       SID TYPE              ID1        ID2      LMODE    REQUEST      BLOCK
---------- ---------- ---------- ---------- ---------- ---------- ----------
       138 TM              73449          0          4          0          1     
       156 TM              73449          0          0          3          0
这就是由于我们加了4号锁除了当前会话外阻塞156会话进行修改,156会话默认可以获得一个3号锁,由于3号和4号互斥不能共存,所以没有获得锁,正在等待申请3号锁
LEO1@LEO1> commit;                                                                   锁跟着事务的结束而释放,commit   rollback都行
LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TM','TX') order by 1,2;     此时没有锁信息了
no rows selected

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

Oracle TM锁的类型

锁模式

锁描述

含义

锁定表的SQL

0

None

  

1

Null

空,本模式是oracle预留模式

 

2

Row Share(RS)

又叫(SS

行级共享锁,是限制最少的TM锁,可以提供最高程度的并发性。其他会话可以对锁定的表进行任何类型的DML操作,还可以与其他会话锁并存

Lock table t in row share mode;

3

Row Exclusive Table Lock(RX)

又叫(SX)

行级排他锁,通常已经有事务在修改行或者select…for update 修改结果集。允许其他事务对锁定的表进行select  insert  update  delete lock table 同时锁定一张表

Lock table t in row exclusive mode;

4

Share Table Lock(S)

共享锁,其他事务可以查询锁定的表但不能修改,只允许当前事务修改,但可以多个事务持有它

Lock table t in share mode;

5

Share Row Exclusive Table Lock(SRX)

又叫SSX

共享行级排他锁,同一时间只允许一个事务持有和修改锁定的表,其他事务可以查询但不能修改

Lock table t in share row exclusive mode;

6

Exclusive Table Lock (X)

排他锁,是限制最高的TM锁,禁止其他事务执行任何类型的DML语句或者锁表

一个表一般只能有一个6号锁

Lock table t in exclusive mode;

Oracle锁模式互斥关系图

锁模式

锁名称

允许级别

互斥级别

2

行级共享锁

2 3 4 5

6

3

行级排他锁

2 3

4 5 6

4

共享锁

2 4

3 5 6

5

共享行级排他锁

2

3 4 5 6

6

排他锁

 

2 3 4 5 6

实验锁互斥

准备工作

LEO1@LEO1> select distinct sid from v$mystat;           LEO1用户的会话id=138

       SID

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

       138

LEO2@LEO1> select distinct sid from v$mystat;           LEO2用户的会话id=156

       SID

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

       156

LEO1@LEO1> create table lock1 (x int primary key);    创建lock1表,设置x列为主键

Table created.

LEO1@LEO1> insert into lock1 values(1);                     我们插入1

1 row created.

LEO1@LEO1> commit;                         提交

Commit complete.

LEO1@LEO1> select * from lock1;      现在只有一条记录,并且没有锁

         X

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

         1

LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TM','TX') order by 1,2;

no rows selected


行级共享锁 Row Share(RS)  2



LEO1@LEO1> lock table lock1 in row share mode;        lock1表设置为行级共享锁模式

Table(s) Locked.

LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TM','TX') order by 1,2;

       SID TYPE              ID1        ID2      LMODE    REQUEST      BLOCK

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

       138 TM              73472          0          2          0          0

模式标识:2

LEO1@LEO1> select object_name from dba_objects where object_id=73472;      lock1表对象id73472

OBJECT_NAME

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

LOCK1


LEO2@LEO1> insert into leo1.lock1 values(2);

1 row created.

LEO2@LEO1> select * from leo1.lock1;

         X

----------

         1

         2

LEO2@LEO1> delete from leo1.lock1 where x=1;

1 row deleted.

LEO2@LEO1> select * from leo1.lock1;

         X

----------

         2

LEO2@LEO1> update leo1.lock1 set x=10 where x=2;

1 row updated.

LEO2@LEO1> select * from leo1.lock1;

         X

----------

        10

LEO2@LEO1> select * from leo1.lock1 for update;

         X

----------

        10

LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TM','TX') order by 1,2;

       SID TYPE              ID1        ID2      LMODE    REQUEST      BLOCK

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

       138 TM              73472          0          2          0          0

       156 TM              73472          0          3          0          0

       156 TX              524321     936          6          0          0

小结:行级共享锁,是限制最少的TM锁,可以提供最高程度的并发性。其他会话可以对锁定的表进行任何类型的DML操作,还可以与其他会话锁并存。


行级排他锁  Row Exclusive Table Lock(RX)  3



LEO1@LEO1> lock table leo1.lock1 in row exclusive mode;              lock1表设置为行级排他锁

Table(s) Locked.

LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TM','TX') order by 1,2;

       SID TYPE              ID1        ID2      LMODE    REQUEST      BLOCK

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

       138 TM              73472          0          3          0          0

模式标识:3

3级锁是一个灵活性比较大的锁,insert delete update 都可以产生一个3级锁,也允许其他事务来修改锁定的表

LEO1@LEO1> select * from leo1.lock1;

         X

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

        10

        20

        30

        40

        50

会话27

LEO1@LEO1> update leo1.lock1 set x=100 where x=10;

1 row updated.

LEO1@LEO1> select * from leo1.lock1;              已经被修改了

         X

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

        20

        30

        40

        50

       100

会话156

LEO2@LEO1> insert into leo1.lock1 values(60);  也已经插入了,注意没有提交的事务对别人是不可见的

1 row created.

LEO2@LEO1> select * from leo1.lock1;

         X

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

        10

        20

        30

        40

        50

        60

会话146

LEO2@LEO1> delete from leo1.lock1 where x=20;   已经删除

1 row deleted.

LEO2@LEO1> select * from leo1.lock1;

         X

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

        10

        30

        40

        50

LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TM','TX') order by 1,2;

       SID TYPE              ID1        ID2      LMODE    REQUEST      BLOCK

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

        27 TM              73472          0          3          0          0

        27 TX              327698    1144          6          0          0

       138 TM              73472          0          3          0          0

       146 TM              73472          0          3          0          0

       146 TX              196626      975          6          0          0

       156 TM              73472          0          3          0          0

       156 TX              262163      782          6          0          0

大家都是共存的,谁也没有阻塞谁,block列全部是0

小结:行级排他锁,通常已经有事务在修改行或者select…for update 修改结果集。允许其他事务对锁定的表进行select  insert  update  delete lock table 同时锁定一张表。


共享锁  Share Table Lock(S)  4


LEO1@LEO1> select * from leo1.lock1;                              还是有5条记录

         X

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

        10

        20

        30

        40

        50

LEO1@LEO1> lock table leo1.lock1 in share mode;           lock1表设置为共享锁

Table(s) Locked.

LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TM','TX') order by 1,2;

       SID TYPE              ID1        ID2      LMODE    REQUEST      BLOCK

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

       138 TM              73472          0          4          0          0

模式标识:4

4级锁禁止其他会话对锁定的表进行DML操作但可以select查询,还允许多个事物一起持有

LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TM','TX') order by 1,2;

       SID TYPE              ID1        ID2      LMODE    REQUEST      BLOCK

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

        13 TM             73472          0          0          4          0

       27 TM              73472          0          0          3          0

       138 TM            73472          0          4          0          1

       146 TM            73472          0          0          3          0

       156 TM            73472          0          0          3          0

27 146 156 会话都已经被阻塞了,有的朋友会问4级锁可以和4级锁共存这里为什么不行呢。呵呵因为有3级锁在捣乱,4级和3级是不能共存的,所以我们把所有的3级锁都释放就可以了,我们来看一下

LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TM','TX') order by 1,2;

       SID TYPE              ID1        ID2      LMODE    REQUEST      BLOCK

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

        13 TM              73472          0          4          0          0

        27 TM              73472          0          2          0          0

       138 TM             73472          0          4          0          0

现在4级锁 2级锁都可以同时存在了

小结:共享锁,其他事务可以查询锁定的表但不能修改,只允许当前事务修改,但可以多个事务持有它。


共享行级排他锁  Share Row Exclusive Table Lock(SRX)  5


LEO1@LEO1> lock table leo1.lock1 in share row exclusive mode;   lock1表设置为共享行级排他锁

Table(s) Locked.

LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TM','TX') order by 1,2;

       SID TYPE              ID1        ID2      LMODE    REQUEST      BLOCK

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

       138 TM              73472          0          5          0          0

模式标识:5

LEO1@LEO1> select object_name from dba_objects where object_id=73472;  现在锁定的表就是lock1

OBJECT_NAME

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

LOCK1

会话138

LEO1@LEO1> select * from leo1.lock1;    现在表里有5条记录,我们看看其他会话能不能修改表

         X

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

        10

        20

        30

        40

        50

会话156

LEO2@LEO1> insert into leo1.lock1 values(60);                  hang住了不能够前进说明被阻塞

会话27

LEO1@LEO1> update leo1.lock1 set x=100 where x=10;     hang住了不能够前进说明被阻塞

会话146

LEO2@LEO1> delete from leo1.lock1 where x=20;              hang住了不能够前进说明被阻塞

那么select… for update 当然也会被阻塞的,我们来看看其他事务能不能查询呢!

会话23

LEO2@LEO1> select * from leo1.lock1;

         X

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

        10

        20

        30

        40

        50

LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TM','TX') order by 1,2;

       SID TYPE              ID1        ID2      LMODE    REQUEST      BLOCK

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

        27 TM               73472          0          0          3          0

       138 TM              73472          0          5          0          1

       146 TM              73472          0          0          3          0

       156 TM              73472          0          0          3          0

会话23没有被阻塞可以正常查询耶,这也验证了我们的观点,读操作不会被任何事务阻塞,也不会加锁,27  146  156 会话3个行级排他锁都在等待着138会话释放锁

LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TM','TX') order by 1,2;

       SID TYPE              ID1        ID2      LMODE    REQUEST      BLOCK

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

        13 TM              73472          0          2          0          0

      138 TM              73472          0          5          0          0

会话132级锁和会话1385级锁可以共存符合我们的锁互斥关系

小结:共享行级排他锁,同一时间只允许一个事务持有和修改锁定的表,其他事务可以查询但不能修改。


排他锁  Exclusive Table Lock (X)  6


LEO1@LEO1> lock table leo1.lock1 in exclusive mode;       lock1表设置为排他锁,等级最高的锁

Table(s) Locked.

LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TM','TX') order by 1,2;

       SID TYPE              ID1        ID2      LMODE    REQUEST      BLOCK

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

       138 TM              73472          0          6          0          0

模式标识:6

会话156

LEO2@LEO1> insert into leo1.lock1 values(60);            

会话27

LEO1@LEO1> update leo1.lock1 set x=100 where x=10;     

会话146

LEO2@LEO1> delete from leo1.lock1 where x=20;         

会话13

LEO2@LEO1> lock table leo1.lock1 in row share mode;       测试锁互斥

LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TM','TX') order by 1,2;

       SID TYPE              ID1        ID2      LMODE    REQUEST      BLOCK

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

        13 TM              73472          0          0          2          0        

        27 TM              73472          0          0          3          0

      138 TM              73472          0          6          0          1

      146 TM              73472          0          0          3          0

      156 TM              73472          0          0          3          0

6级锁会阻塞除了自己外的所有会话的事务并且排斥其他的所有锁模式连2级锁都不行,是最高限制的TM锁,当然select还是没有问题的。

小结:排他锁,是限制最高的TM锁,禁止其他事务执行任何类型的DML语句或者锁表。一个表一般只能有一个6号锁。


5.给出一个导致死锁的SQL示例。

死锁定义:从广义上讲包括操作系统 应用程序 数据库,如果2个进程(会话)相互持有对方的资源,都一直等待对方释放,这种情况会造成死锁。
误解:会话的阻塞可不是死锁,因为其中有一个会话还是可以继续操作的。
释放:Oracle会自动检测死锁并强制干预释放

LEO1@LEO1> create table p1 ( x int primary key );       我们新建一个p1表,设置x字段为主键
Table created.
LEO1@LEO1> insert into leo1.p1 values(10);                 138会话插入的是10
1 row created.
LEO2@LEO1> insert into leo1.p1 values(20);                 156会话插入的是20
1 row created.
LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TM','TX') order by 1,2;
       SID TYPE              ID1        ID2      LMODE    REQUEST      BLOCK
---------- ---------- ---------- ---------- ---------- ---------- ----------
       138 TM              73470          0          3          0          0
       138 TX             327713    1124          6          0          0             138有一个TX排他锁,但当前没有阻塞会话
       156 TM              73470          0          3          0          0
       156 TX             589825      945          6          0          0             156也有一个TX排他锁,但当前也没有阻塞会话
LEO1@LEO1> select object_name from dba_objects where object_id=73470;            看p1表上存在正常的TM  TX锁,都没有阻塞到对方的会话
OBJECT_NAME
--------------------------------------------------------------------------------
P1
LEO1@LEO1> insert into leo1.p1 values(20);              此时我在138会话上再插入20,发现hang住了不能前进,这是什么原因呢?我们看看v$lock视图
LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TM','TX') order by 1,2;
       SID TY        ID1        ID2      LMODE    REQUEST      BLOCK
---------- -- ---------- ---------- ---------- ---------- ----------
       138 TM      73470          0          3          0          0
       138 TX     589825      945          0          4          0     这时138会话就继续插入了,但这个插入动作是成功的没有阻塞,而是由于138|156会话修改值的相同
       138 TX     327713    1124          6          0          0     违反了主键约束从而产生阻塞,实际是对修改值的相同产生了阻塞,所以申请的是4级锁,而非6级锁
       156 TM      73470          0          3          0          0
       156 TX     589825      945          6          0          1     156会话此时正在阻塞138会话,因为156会话的事务还没有完成还是一个未决状态

LEO2@LEO1> insert into leo1.p1 values(10);               我在156会话上也插入10,这时死锁的效果就出来了
LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TM','TX') order by 1,2;
       SID TY        ID1        ID2      LMODE    REQUEST      BLOCK
---------- -- ---------- ---------- ---------- ---------- ----------
       138 TM      73470          0          3          0          0
       138 TX     327713    1124          6          0          1     138会话此时正在阻塞156会话,因为138会话的事务还没有完成还是一个未决状态
       156 TM      73470          0          3          0          0
       156 TX     327713    1124          0          4          0     实际上是对修改值的相同产生了阻塞,156会话正在申请4级锁
       156 TX     589825      945          6          0          0
LEO1@LEO1> insert into leo1.p1 values(20);               我们看一下138会话报错,Oracle自动检测死锁并强制干预释放
insert into leo1.p1 values(20)
                 *
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource(等待资源时检测到死锁->释放之)  这时请注意一下,只是释放掉了第一个锁定,但第二个锁定还在等待,所以我们要手工释放

小结:上面讲到了好几种锁的机制,我们崇尚的思想就是先要想一想为什么会出现锁,不出现行不行,锁的作用有哪些,这种启发式的思路能够让我们记忆深刻。
LOCK作用:独占业务资源  保证读一致性  维护事务完整性
LOCK宗旨:没有并发就没有锁,一个人操作数据库是不会产生锁的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值