锁是防止在两个事务操作同一个数据源(表或行)时交互破坏数据的一种机制。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:查询type为0的最小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:查询type为0的最小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:将ID为1的这条记录的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:将ID为1的这条记录的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) 小结:
我们看到id为1的type现在的值为2,会话1将type更新为1的记录已经“丢失”
1.2. 悲观锁
1) 会话1:查询id为2的记录并进行锁定
19:05:43 SYS@ORA11GR2>select * from t_lock where id=2 and type =0 for update nowait;
ID TYPE
---------- ----------
2 0
2) 会话2:查询id为2的记录,此时查询报错
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:对id为2的记录进行更新。
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:查询id为2的记录,由于已经将id为2的type已经变为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:查询id为3的伪列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:查询id为3的伪列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:更新id为3的type为1
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:更新id为3的type为1
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_1的id字段为1的name为“liuxuande”,不提交
19:39:55 SYS@ORA11GR2>update t_lock_1 set name='liuxuande' where id =1;
1 row updated.
3) 会话2:更新表t_lock_2的id字段为1的name为“关云长”,不提交
19:39:47 SYS@ORA11GR2>update t_lock_2 set name='guanyunchang' where id = 1;
1 row updated.
4) 会话1:更新表t_lock_2的id字段为1的name为“guanyunchang”,此时挂起状态
19:40:30 SYS@ORA11GR2>update t_lock_2 set name='guanyunchang' where id =1;
5) 会话2:更新表t_lock_1的id字段为1的name为“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的死锁问题实际上很少见,如果发生,基本上都是不正确的程序设计造成的,经过调整后,基本上都会避免死锁的发生。