zoukankan      html  css  js  c++  java
  • 悲观锁定和乐观锁定

    为了避免丢失更新(更新冲突),要使用某种锁定策略,共有两种锁定策略:悲观锁定或乐观锁定。

    一、悲观锁定(pessimistic locking):
    1.表现:用户在屏幕上修改值之前,这个锁定方法就要起作用。例如,用户一旦有意对他选择的某个特定行(屏幕上可见)执行更新,如单击屏幕上的一个按钮,就会放上一个锁。

    (悲观的认定每次数据存取,其他客户端也会同时存取这一数据,因而马上锁定,直到当前操作完成)

    2.悲观锁定仅用于有状态(stateful)或有连接(connected)的环境,这是20世纪90年代中期客户/服务器应用中的一种流行做法。但现在采用有状态方式的连接方法已经不太常见了(不过并没有完全消失),特别是随着20世纪90年代中后期应用服务器的出现,有状态连接更是少见。

    悲观锁其实就是 传统锁,就是锁了别人进不来,无法更新,无法读取,而hang out,等time out后抛出异常。

    3.例子:
    select empno, ename, sal
    from emp
    where empno = :empno
       and ename = :aname
       and sal = :sal
      for update nowait

    二、乐观锁定(optimistic locking)
    即把所有锁定都延迟到即将执行更新之前才做。

    表现:在修改屏幕上的信息而不要锁。我们很乐观,认为数据不会被其他用户修改;因此,会等到最后一刻才会去看我们的想法对不对。

    这种锁定方法在所有环境下都行得通,但是执行更新的用户“失败”的可能性会增大。这个用户要更新它的数据行时,发现数据已经修改过,所以他必须重头再来。

    乐观锁其实 并不是一种 锁, 只不过是一种策略,比如我们平时用的sub version就是采用其中的一种最常用的策略: versionstrategy: 就是在要锁的表的 数据库记录 加一个 version控制。(原则是 the older versions' data is restricted from overwriting any data of the newer versions. )

    本质上是一种检测手段,不管你如何修改,最后由软件来实现merge(合并)

    可以在应用中同时保留旧值和新值,然后在更新数据库时使用如下的更新语句,这是乐观锁定的一种流行实现:
    updtae table
       set column1 = :new_column1, column2 = :new_column2,...
    where primary_key = :primary_key
       and column1 = :ld_column1
       and column2 = :ld_column2
    ...

    这种情况下,可能很幸运地更新了一行,但如果另一个人已经修改了数据,就会失败。现在须确定下一步要做什么,是让最终用户重新查询这一行现在的新值,然后再重新开始新事物呢(但是这一行有可能又被修改了,这可能会使用户很受打击)?还是根据业务规则更新冲突,试图合并两个更新的值(这需要大量的代码)?

    乐观锁的实现:大多基于数据版本记录机制,一般是在数据库表中加入一个version字段,读取数据时将版本号一同读出,之后更新数据时版本号就加一,如果提交数据时版本号小于或等于数据表中的,则认为数据是过期的,否则才给予更新。version控制支持lazy

    缺点是解决用户输入大量数据结果很容易发生无法更新

    乐观锁还可以使用世间截等方式,本质上都是依据 某 判断是否可以覆盖当前数据库中的数据。

    另外,上面的update有可能被阻塞。

    三、比较

    如果所有应用(会话)都使用乐观锁定,执行更新并提交时,行只会被锁定很短的时间。但如果某些应用使用了悲观锁定,它会在较长时间内持有行上的锁,你可能就会考虑使用select for update nowait,以此来验证行是否被修改,并在即将update之前锁定来避免被另一个会话阻塞。

    四、另外三种实现乐观并发控制的方法:

    1、使用版本列的乐观锁定。
    对每个要保护的表增加一列,这一列通常是number或date/timestamp列,通常通过行触发器来维护。
    在应用中只需保存这个版本列的值,而不用保存所有其他列的值。
    为了维护这个版本列,建议把更新逻辑封装到一个存储过程中。另一个实现方法是使用触发器,但是触发器会引入大量开销。

    2、使用校验和的乐观锁定
    这与前面的版本列方法很相似,在此要使用计数据本身来计算一个“虚拟的”版本列。有很多方法计算散列或校验和。以下是其中的三种:
    (1)OWA_OPT_LOCK.CHECKSUM Oracle8.1.5)及以上版本中提供,出现冲突的可能性是1/65536。
    (2)DBMS_DBFUSCATION_TOOLKIT.MD5 Oracle8.1.7)及以上版本中提供,出现冲突的可能性是1/(3.4E+38),非常小。
    (3)DBMS_CRYPTO.HSAH Oracle10.1)及以上版本中提供.
    很多编程语言中都提供了一些散列和校验和函数,所以还可以使用数据库之外的散列和校验和函数。
    计算散列和校验和是CPU密集型的操作,其计算代价很昂贵,但是其“网络友好性比较好”。

    下面的ORA_ROWSCN方法不仅很小(类似于散列),而且计算时不是cpu密集的。
    3、使用ORA_ROWSCN的乐观锁定
    从Oracle 10.1开始,还可以使用内置的ORA_ROWSCN函数,其原理与版本列技术很相似,但是可以由Oracle自动执行,而不需要在表中增加额外的列,也不需要额外的代码来更新维护这个值。

    四、最后

    不管是悲观锁定还是乐观锁定都可以利用select for update nowaut查询来验证行未被修改。

    悲观锁定会在用户有意修改数据那一刻使用这条语句。

    乐观锁定则在即将在数据库中更新数据时使用这条语句。

    这样不仅能解决应用中的阻塞问题,还可以修正数据完整性问题。

  • 相关阅读:
    mysql将视图数据迁移到表中
    一、Vant示例文件
    一、VS安装GitHub插件
    二、.net 特性之二
    .net Core jwt策略参数
    一、
    一、.Net Core 3.1 全局序列化
    前端项目
    python 小脚本升级-- 钉钉群聊天机器人
    java 接口测试,使用excel做数据驱动(二)
  • 原文地址:https://www.cnblogs.com/isykw/p/6094246.html
Copyright © 2011-2022 走看看