返回
顶部

修改密码

首页 > 数据库 > Oracle > 正文
Oracle锁的基本机制

+1

-1

收藏

+1

-1

点赞0

评论0

1.什么是锁锁(LOCK)用于管理对共享资源的并发控制。通过建立锁,可以保护资源(数据)的完整性和一致性。通过一个经典的数据库问题(丢失更新)就可以看到锁的意义:1)针对同一个数据库,有表 Student,表结构如下: ID、姓名、年龄2)用户A登录到数据库中,查询Student表…

1.什么是锁

  锁(LOCK)用于管理对共享资源的并发控制。通过建立锁,可以保护资源(数据)的完整性和一致性。

  通过一个经典的数据库问题(丢失更新)就可以看到锁的意义:

  1)针对同一个数据库,有表 Student,表结构如下: ID、姓名、年龄

  2)用户A登录到数据库中,查询Student表的数据,此时标记A的会话为Session1

  3)用户B登录到数据库中,查询Student表的数据,此时标记B的会话为session2

  4)用户A将Student表中姓名为张三的学生姓名修改为李四,并提交

  5)用户B将Student表中姓名为张三的学生的年龄修改为18,并提交

  当B提交数据时,他看到的数据是最初提取出来的数据,并做出修改,此时数据库中会存在一条姓名为张三年龄为18的记录。但是在操作4中,用户A明明将张三的姓名修改为了李四,这样明显可以看出,用户A做的修改并没用生效,这种情况称之为丢失更新。虽然整个过程仅仅涉及了Select和Update简单的操作,但产生的问题是很明显的。为此,我们应该对数据共享进行一定的保护,也就引入了锁机制。

2.锁机制

 悲观锁定

  悲观锁定就如同他的名字一样,数据库“悲观”地认为并发情况下准备修改的数据随时有可能已经被他人修改。因此在用户获取某些数据并有意做出修改时,数据库就对这些数据加上锁,并一直将锁保持至用户提交或回滚当前事务,在此期间其他用户不能对这些数据或数据结构做出修改。正是因为悲观锁定的这种特性,用户必须要保持与数据库的有效的连接状态,而且至少在事务生存期中只有一个用户。一般来说悲观锁定对数据的一致性管理是较为有效的,但是其缺点也较为明显:当并发量较大时,很容易造成阻塞,同时在一些特定的应用环境下,如Web应用,因为很难保证事务生存期中只有一个用户,当多个用户进入这个连接,那么锁的作用就会失效。

 乐观锁定

  与悲观锁定不同,数据库“乐观”地认为并发情况下准备被修改的数据不会被他人修改,直到最后做提交修改时才加锁并验证数据是否正确。乐观锁定事务开始于最终提交修改,因此对连接的要求几乎没有。但是因为在事务开始前,有可能修改的数据已经被他人修改过,此时会回滚当前的事务,用户做的修改将会失败。采用乐观锁定的要求比悲观锁定要低很多,并且阻塞的时间也非常的短。但因为有可能事务被回退,就需要为此做其他的弥补措施。

 阻塞

  阻塞发生在用户之间对资源的控制权争夺中,如果一个用户A已经对某个数据持有了锁,另一个用户B又请求获取这个数据的控制权,那么就会产生阻塞。在此期间,用户B的操作会被搁置, 直到用户A提交或回滚当前事务,用户B才能获取到数据的控制权。

 死锁

  死锁同样发生在资源的控制权争夺中,但是与阻塞最大的区别是:

  阻塞是由于资源不足引起的排队等待现象。
  死锁是由于两个对象在拥有一份资源的情况下申请另一份资源,而另一份资源恰好又是这两对象正持有的,导致两对象无法完成操作,且所持资源无法释放。

 

  下面通过一个简单的例子介绍死锁产生的一个原因:

  1)数据库中存在A、B两表,且两表都只有一条数据

  2)用户A进入数据库,此时标记A的会话为Session1

  3)用户B进入数据库,此时标记B的会话为Session2

  4)用户A修改A表的数据,用户B修改B表的数据

  5)用户A再修改B表的数据,用户B再修改A表的数据

  进行到操作5时,将产生死锁现象。用户A在等待用户B释放对A表数据的控制权,而用户B又在等待用户A释放对B表数据的控制权,这就引发了死锁的现象。

  需要注意的是,Oracle数据库本身几乎不会发生死锁现象,一旦你的应用中出现了死锁的问题,那么一般是设计上存在Bug(如外键中没有创建索引等)。

 

  因为外键上没有创建索引导致死锁问题

  大部分的死锁问题都是由外键上没有索引导致,下面通过一个简单的案例进行说明。

  1.创建两个简单的表并插入数据

create table 父表(id VARCHAR2(2) Primary Key);
create table 子表(子id VARCHAR2(2) ,父id VARCHAR2(2) References 父表(id));

Insert into 父表  values(1); 
Insert into 父表 values(2);
Insert into 父表 values(3); insert into 子表 values(1,1);
insert into 子表 values(2,1);
insert into 子表 values(3,1); insert into 子表 values(4,2);
insert into 子表 values(5,2);
insert into 子表 values(5,3);

  2.会话1尝试修改子表数据

update 子表 set 子id=10 where 子id = 1

  3.会话2删除一条父表数据

delete from  父表 where id=2;

  4.查看锁类型  

1 Select b.Object_Name, a.Session_Id,
2        Decode(a.Locked_Mode, 0, 'None', 1, 'Null', 2, 'Row-S (SS)', 3, 'Row-X (SX)', 4, 'Share', 5, 'S/Row-X (SSX)', 6,
3                'Exclusive', To_Char(a.Locked_Mode)) Lock_Type
4 From V$locked_Object A, Dba_Objects B
5 Where b.Object_Id = a.Object_Id

  结果如下:

      

  5.查看执行语句的sid

1 select UserENV('sid') sid from dual

--查询结果:执行修改子表操作会话1的Sid=11,执行删除父表数据操作的会话2的Sid=199

 

  

  通过实验即使子表修改的数据和父表删除的数据没有关联,但是仍产生了死锁。

  原因如下:修改子表数据时,会对子表申请一个Row-X锁(行级排他锁),此时删除父表数据,因为没有索引,为了检测是否有关联数据,会请求子表的一个全表排它锁,而此时,子表事务未提交,存在一个行级排它锁,阻止了全表排它锁的获取,造成了死锁的现象。

  同理,因为没有索引,每次对父表数据进行操作,一旦同时有其他会话已经获得了子表的锁,都会引发死锁。

扫一扫在手机打开

评论
已有0条评论
0/150
提交
热门评论
相关推荐
数据库表连接的简单解释
  • 操作技巧
  • 2022-05-25 18:24
  • 22 0 0
+1
SQL 行转列,列转行
  • 操作技巧
  • 2022-05-25 18:24
  • 20 0 0
+1
一起看懂Redis两种持久化方式的原理
  • 操作技巧
  • 2022-05-25 18:24
  • 32 0 0
+1
为什么要用Redis
  • 操作技巧
  • 2022-05-25 18:24
  • 13 0 0
+1