【锁机制】共享锁、排它锁、悲观锁、乐观锁、死锁等等

锁是防止在两个事务操作同一个数据源(表或行)时交互破坏数据的一种机制。Oracle采用封锁技术保证并发操作的可串行性。Oracle的锁分为两大类:数据锁(也称DML锁)和字典锁。字典锁是Oracle DBMS内部用于对字典表的封锁。字典锁包括语法分析锁和DDL锁,由DBMS在必要的时候自动加锁和释放锁,用户无机控制。

Oracle主要提供了5种数据锁:共享锁(Share Table Lock,简称S锁)、排它锁(Exclusive Table Lock,简称X锁)、行级锁(Row Share Table Lock,简称RS锁)、行级排它锁(Row Exclusive Table Lock,简称RX锁)和共享行级排它锁(Share Row Exclusive Table Lock,简称SRX锁)。其封锁粒度包括行级和表级。

按照类型划分,锁可分为行级排他锁和表级共享锁及死锁。按照对象划分,锁可分为事务锁,DML锁,DDL锁,内存锁(Latch)。


锁相关测试

1.1. 更新丢失

1)   创建测试表

SYS@ORA11GR2>create table t_lock as select rownum as id,0 as type from dual connect by rownum <=3;

 

Table created.

 

SYS@ORA11GR2>select * from t_lock;

 

        ID       TYPE

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

         1          0

         2          0

         3          0

 

2)   会话1:查询type0的最小id

SYS@ORA11GR2>set time on;

18:58:22 SYS@ORA11GR2>

18:58:23 SYS@ORA11GR2>select min(id) from t_lock where type=0;

 

   MIN(ID)

----------

         1

 

3)   会话2:查询type0的最小id

SYS@ORA11GR2>set time on

18:59:31 SYS@ORA11GR2>select min(id) from t_lock where type=0;

 

   MIN(ID)

----------

         1

 

4)   会话1:将ID1的这条记录的type置为1

19:00:53 SYS@ORA11GR2>update t_lock set type=1 where id=1;

1 row updated.

 

19:01:21 SYS@ORA11GR2>commit;

 

Commit complete.

 

19:01:37 SYS@ORA11GR2>select * from t_lock;

 

        ID       TYPE

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

         1          1

         2          0

         3          0

 

5)   会话2:将ID1的这条记录的type置为2

19:02:47 SYS@ORA11GR2>update t_lock set type=2 where id=1;

 

1 row updated.

 

19:03:11 SYS@ORA11GR2>commit;

 

Commit complete.

 

19:03:17 SYS@ORA11GR2>select * from t_lock;

 

        ID       TYPE

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

         1          2

         2          0

         3          0

 

19:03:32 SYS@ORA11GR2>

6)   小结:

我们看到id1type现在的值为2,会话1type更新为1的记录已经“丢失”

1.2. 悲观锁

1)   会话1查询id2的记录并进行锁定

19:05:43 SYS@ORA11GR2>select * from t_lock where id=2 and type =0 for update nowait;

 

        ID       TYPE

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

         2          0

 

2)   会话2:查询id2的记录,此时查询报错

19:07:43 SYS@ORA11GR2>select * from t_lock where id=2 and type=0 for update nowait;

select * from t_lock where id=2 and type=0 for update nowait

              *

ERROR at line 1:

ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired

 

3)   会话1:对id2的记录进行更新。

19:19:08 SYS@ORA11GR2>update t_lock set type=1 where id=2 and type=0;

 

1 row updated.

 

19:19:30 SYS@ORA11GR2>commit;

 

Commit complete.

 

19:19:39 SYS@ORA11GR2>select * from t_lock where id=2;

 

        ID       TYPE

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

         2          1

 

4)   会话2查询id2的记录,由于已经将id2type已经变为1,所以查不到数据了。

19:19:15 SYS@ORA11GR2>select * from t_lock where id=2 and type=0 for update nowait;

 

no rows selected

 

1.3乐观锁

1)   会话1:查询id3的伪列ora_rowscn的值

19:22:00 SYS@ORA11GR2>select id,type,ora_rowscn from t_lock where id = 3;

 

        ID       TYPE ORA_ROWSCN

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

         3          0    1246809

 

2)   会话2:查询id3的伪列ora_rowscn的值

19:23:01 SYS@ORA11GR2>select id,type,ora_rowscn from t_lock where id = 3;

 

        ID       TYPE ORA_ROWSCN

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

         3          0    1246809

 

3)   会话1:更新id3type1

19:24:22 SYS@ORA11GR2>update t_lock set type=1 where ora_rowscn=1246809 and id = 3;

 

1 row updated.

 

19:25:29 SYS@ORA11GR2>commit;

 

Commit complete.

 

验证:

19:28:22 SYS@ORA11GR2>select id,type,ora_rowscn from t_lock where id = 3;

 

        ID       TYPE ORA_ROWSCN

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

         3          1    1247164

 

4)   会话2:更新id3type1

19:26:05 SYS@ORA11GR2>update t_lock set type=1 where ora_rowscn=1246809 and id =3;

 

0 rows updated.

 

验证:

19:29:37 SYS@ORA11GR2>select id,type,ora_rowscn from t_lock where id = 3;

 

        ID       TYPE ORA_ROWSCN

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

         3          1    1247164

(因为会话1的事务更改了id=3的值,而且事务已经提交,事务的ora_rowscn已经变为1247164,原来的ora_rowscn=1246809已经不存在,所以没有可更改的行了)

1.4死锁

1)   创建测试表

19:35:46 SYS@ORA11GR2>create table t_lock_1 (id number(2),name varchar2(15));

 

Table created.

 

19:35:57 SYS@ORA11GR2>create table t_lock_2 as select * from t_lock_1;

 

Table created.

 

19:36:24 SYS@ORA11GR2>insert into t_lock_1 values(1,'liubei');

 

1 row created.

 

19:37:11 SYS@ORA11GR2>insert into t_lock_2 values (1,'guanyu');

 

1 row created.

 

19:37:38 SYS@ORA11GR2>commit;

 

Commit complete.

 

19:37:43 SYS@ORA11GR2>select * from t_lock_1;

 

        ID NAME

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

         1 liubei

 

19:38:01 SYS@ORA11GR2>select * from t_lock_2;

 

        ID NAME

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

         1 guanyu

 

2)   会话1:更新表t_lock_1id字段为1name为“liuxuande,不提交

19:39:55 SYS@ORA11GR2>update t_lock_1 set name='liuxuande' where id =1;

 

1 row updated.

3)   会话2:更新表t_lock_2id字段为1name为“关云长”,不提交

19:39:47 SYS@ORA11GR2>update t_lock_2 set name='guanyunchang' where id = 1;

 

1 row updated.

 

4)   会话1:更新表t_lock_2id字段为1name为“guanyunchang”,此时挂起状态

19:40:30 SYS@ORA11GR2>update t_lock_2 set name='guanyunchang' where id =1;

 

5)   会话2:更新表t_lock_1id字段为1name为“liuxuande”,此时挂起状态

19:44:14 SYS@ORA11GR2>update t_lock_1 set name='liuxuande' where id =1;

 

6)   会话1:此时回到会话1,出现死锁错误

19:40:30 SYS@ORA11GR2>update t_lock_2 set name='guanyunchang' where id =1;

update t_lock_2 set name='guanyunchang' where id =1

       *

ERROR at line 1:

ORA-00060: deadlock detected while waiting for resource

 

会话1处于死锁状态,而会话2处于挂起状态;

总结:悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。
     乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁。
     乐观锁适用于写比较少的情况下,即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果经常产生冲突,上层应用会不断的进行retry,这样反倒是降低了性能,所以这种情况下用悲观锁就比较合适。
     定义:当两个用户希望持有对方的资源时就会发生死锁.
即两个用户互相等待对方释放资源时,oracle认定为产生了死锁,在这种情况下,将以牺牲一个用户作为代价,另一个用户继续执行,牺牲的用户的事务将回滚.
     起因:Oracle的死锁问题实际上很少见,如果发生,基本上都是不正确的程序设计造成的,经过调整后,基本上都会避免死锁的发生。

请使用浏览器的分享功能分享到微信等