zoukankan      html  css  js  c++  java
  • 数据库锁-行级锁

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

    行级锁:(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操作不受影响表级锁讲的比较简单,有兴趣的朋友可以深入探究一下~


    原文链接:https://blog.csdn.net/qq_32317661/article/details/80486102

  • 相关阅读:
    Jmeter运行badboy录制的脚本
    Bugfree安装与使用
    JMeter录制脚本
    第六天-linux系统优化初步讲解
    第五天-linux基础命令
    第四天-secureCRT-ssh客户端使用详解
    第三天-linux版本选择及安装
    第二天-计算机硬件基本知识和linux发展简史
    第一天-学习linux运维
    ubuntu15.04 无线上网问题
  • 原文地址:https://www.cnblogs.com/nxzblogs/p/11639807.html
Copyright © 2011-2022 走看看