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表对象id为73472
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
会话13的2级锁和会话138的5级锁可以共存符合我们的锁互斥关系
小结:共享行级排他锁,同一时间只允许一个事务持有和修改锁定的表,其他事务可以查询但不能修改。
排他锁 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宗旨:没有并发就没有锁,一个人操作数据库是不会产生锁的