四、数据库锁机制--行级锁(悲观锁与乐观锁)与表级锁

上一章讲到了数据库事务的隔离级别以及并发事务在不同隔离级别下可能带来的问题和解决思路,感兴趣的朋友可以看一下!

点击打开链接

直接切入正题:

      从字面上看,行级锁的作用范围肯定比表级锁的作用范围要小;行级锁和表级锁是根据锁的粒度来区分的,行记录,表都是资源,锁是作用在这些资源上的。如果粒度比较小(比如行级锁),可以增加系统的并发量但需要较大的系统开销,会影响到性能,出现死锁,,因为粒度小则操作的锁的数量会增加;如果作用在表上,粒度大,开销小,维护的锁少,不会出现死锁,但是并发是相当昂贵的,因为锁定了整个表就限制了其它事务对这个表中其他记录的访问。

行级锁(tx锁,也叫事务锁)

一、悲观锁:数据库行级锁,目的是让数据被查出来的时候就加上锁,然后再执行下面的程序逻辑,这样后面为了操作相同数据而进来的请求,就会在一开始就被拦住(这种效果千万不要以为可以做防重复提交)

在操作DML(insert,update,delete)语句时,oracle会自动加上行级锁,在select * from table for update 【of column】【nowait|wait 3】时,oracle也会自动加锁

  单表 for update

 1. 一般在for update 时加nowait,这样就不用等待其他事务执行了,一判断有事务,立马抛出错误。

下面简单说一下 for update的四种情况:

1.1    select * from table where id = '1001' for update 锁住了这条数据,那么另外一个人对该笔数据进行DML操作或者也执行同样的for update操作时,会检测到这笔数据上有行级锁,那么就会等待着锁释放;

这样就会出现一个问题:其他的程序如果需要对这笔数据操作,就需要等,至于等多久要看锁什么时候释放!

1.2    select * from table where id = '1001' for update nowait,意思就是如果这笔数据上本身加了锁,另外一个人去执行这句SQL的时候,发现加了锁,就会直接抛出异常(ORA-00054:资源正忙),不会等待这笔数据的锁释放。

1.3    select * from table where id = '1001' for update wait 5;意思就是如果这笔数据被锁住,另外一个人如果执行这句SQL后,会等待5秒,如果5秒后这句SQL还没有得到这笔数据的锁,就会抛出异常(ORA-00054:资源正忙

1.4  我们先执行 A语句:select * from table where id = '1001' for update 把1001加上锁,然后再执行 B语句:select * from table where id = '1001' and id ='1002' for update;这时候肯定查不出来,因为A已经把B要加锁的数据锁了,这样B联1002的数据都查不出来

解决方案:skip locked

如果把B语句改为:select * from table where id = '1001' and id ='1002' for update skip locked;意思就是执行的时候如果发现要查询的数据有锁,就把加了锁的数据排除,把剩下的数据加锁,然后查出来!

上面讲到了 for update 的四种方式,实际情况如何选择呢?

关于NOWAIT(如果一定要用FOR UPDATE,我更建议加上NOWAIT)
    当有LOCK冲突时会提示错误并结束STATEMENT而不是在那里等待(比如:要查的行已经被其它事务锁了,当前的锁事务与之冲突,加上nowait,当前的事务会结束会提示错误并立即结束 STATEMENT而不再等待).
    WAIT 子句指定等待其他用户释放锁的秒数,防止无限期的等待。
  “使用FOR UPDATE WAIT”子句的优点如下:
  1.防止无限期地等待被锁定的行;
  2.允许应用程序中对锁的等待时间进行更多的控制。
  3.对于交互式应用程序非常有用,因为这些用户不能等待不确定

  4.若使用了skip locked,则可以越过锁定的行,不会报告由wait n 引发的‘资源忙’异常报告


  关联表for update

2. 现在大部分业务都是联表查询,如果用for update 的话,就会把所有关联表查询出来的列所在的行全部加锁,那这个锁可就重了,比如:

 select * from t1,t2 where t1.id = t2.id and t1.age = '20' for update;就会把T1和T2两个表中符合条件的行锁定;

如果上述SQL我只想对T1表的结果集加锁,怎么办?答案:of column_name

例子:

 select * from t1,t2 where t1.id = t2.id and t1.age = '20' for update of t1.id;

这样就会只把T1表中的符合条件的行加锁,T2表中符合条件的行不会加锁。

PS:如果单表for update of column_name查询,其实和 for update操作是一样的!


二、乐观锁:这不是数据库本身的锁,是利用数据比较结果来当做抽象的锁;举个例子就明白:

说明:小明成绩错了,要改成绩。班主任能改,年级主任也能改!

程序:

{

//先查出来小明的成绩

select t.id,t.result from  T t where t.id='10001';---10001,59

//更新成绩,改为60

update T t set t.result =‘60’ where t.id='10001'

and t.result = '59'  //加上这个条件的目的就是为了验证,数据库里10001的成绩在此期间有没有被其他人改过,如果改过,那就更新条数为0(因为找不到符合条件的数据);

PS:没有找到数据,所以没更新10001这笔数据,最好是程序返回一个没有更新到这笔数据的提示,如果不加任何提示,前端就会认为更新成功了!

}

分析:

1.利用数据库中的数据和已经取出的数据的一致性做为“锁”,与for update相比,乐观锁机制是等到更改数据的时候才去校验,悲观锁是读取数据就开始做了校验,从这个角度来看,乐观锁是对数据库没有额外开销,那么效率相对是高的。

2. 需要更改的字段可以作为乐观锁的验证字段;或者表里建立version版本号,每更新一次数据版本号+1;或者加lastupdatedate(最后更新时间),同理:数据更改的同时lastupdatedate也跟着变更!

3.其实乐观锁存在一个很致命的问题:

场景: 已上述小明改成绩为例,假设班主任改的同时,年级主任也改,两个请求几乎同时执行了查询:

select t.id,t.result from  T t where t.id='10001';---10001,59

都查出来是59分!!

然后几乎同时执行了改成绩,班主任改成60分,年级主任改成了80分,关键是还都update到10001了

班主任:update T t set t.result =‘60’ where t.id='10001'

and t.result = '59' ;

年级主任: 

update T t set t.result =‘80’ where t.id='10001'

and t.result = '59' ;

这时候班主任事务先提交,数据库小明成绩改成了60,年级主任事务紧接着提交,小明的成绩又从60改成了80,那么对于班主任来说,他的数据就是更新丢失!!!所以大家使用起来要注意并发的情况!!


表级锁:

一般指的是表结构共享锁。是不可对该表进行DDL操作,但是对于该表数据的DML操作不受影响

表级锁讲的比较简单,有兴趣的朋友可以深入探究一下~

本章我们就讲到这里,如果大家有什么想法可以留言,一起讨论指正!

对于数据库事务,隔离性,并发,锁的认识我们就讲到这里,下一章我们开始讲Spring事务以及它的传播机制等,欢迎观看!


  • 7
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
在Quartz中,数据库集群是一种高可用性的解决方案。它允许多个Quartz实例共享同一个数据库,并且能够自动协调任务的执行,从而保证任务的可靠性和稳定性。 但是,在多个Quartz实例同时操作同一个数据库时,必须确保它们之间的数据一致性。这就需要使用锁机制来保证数据的正确性和安全性。 Quartz提供了两种类型的悲观锁乐观锁。它们分别采用不同的方式来保证数据的一致性。 1. 悲观锁 悲观锁是一种悲观的认为并发环境下会出现冲突的锁机制。它在操作数据时,会先加,然后再进行操作,操作完成后再释放。 在Quartz中,悲观锁是通过数据库中的行级来实现的。当一个Quartz实例要对某个任务进行操作时,它会先获取该任务的行级,然后再进行操作。其他实例在此期间无法获取该任务的,从而保证了数据的一致性。 2. 乐观锁 乐观锁是一种乐观的认为并发环境下不会出现冲突的锁机制。它在操作数据时,不会加,而是通过版本号等方式来判断数据是否发生了变化。 在Quartz中,乐观锁是通过版本号来实现的。每个任务都有一个版本号,当一个Quartz实例要对某个任务进行操作时,它会先获取该任务的版本号,然后进行操作。如果在此期间该任务的版本号发生了变化,则说明其他实例已经对该任务进行了操作,当前实例的操作会失败,需要重新获取版本号并重试。这样可以保证数据的一致性。 总的来说,悲观锁适用于高并发、数据冲突严重的场景,但是会带来较大的性能开销;而乐观锁适用于并发量较小、数据冲突不严重的场景,性能开销较小。在Quartz中,可以根据具体的业务需求选择合适的锁机制来保证数据的正确性和安全性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值