乐观锁与悲观锁

来源:互联网 发布:sql注入修复建议 编辑:程序博客网 时间:2024/05/16 09:21


    在多用户环境中,在同一时间可能会有多个用户更新相同的记录,这会产生冲突。引发并发性问题。

    典型冲突:丢失更新和脏读

      丢失更新:一个事务的更新覆盖了其他事务的更新结果。

      脏读:当一个事务读取其他完成一半事务的记录时,就会发生脏读取。


    那该如何解决这些问题呢?针对这种问题的存在,我们引入了并发控制机制。

    悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。

    乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性,但值得注意的是,乐观锁不能解决脏读的问题。


    乐观锁

    使用乐观锁的前提是,实际应用当中,发生冲突的概率比较低。

    乐观锁在数据进行提交更新的时候才会检测数据是否发生冲突,如果发生冲入,则返回用户错误的信息,让用户决定如何去做;提交数据前,检测有没有其他事务修改了该数据,如果其他事务有更新,正在提交的事务会进行回滚。

    实现乐观锁,一般有两种方式:使用版本号,使用时间戳。

    

    使用版本号

    为数据增加一个版本标识,一般是通过为数据库表增加一个数字类型的 “version” 字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据。

    

    如果更新操作顺序执行,则数据的版本(version)依次递增,不会产生冲突。但是如果发生有不同的业务操作对同一版本的数据进行修改,那么,先提交的操作(图中B)会把数据version更新为2,当A在B之后提交更新时发现数据的version已经被修改了,那么A的更新操作会失败。


    使用时间戳

    和第一种方式很相似,使用时间戳也需要在乐观锁控制的table中增加一个字段,字段类型使用时间戳(timestamp),在更新提交的时候检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则进行更新,否则发生版本冲突。


    优点与不足

    乐观并发控制相信事务之间的数据竞争(data race)的概率是比较小的,因此尽可能直接做下去,直到提交的时候才去锁定,所以不会产生任何锁和死锁。但如果直接简单这么做,还是有可能会遇到不可预期的结果,例如两个事务都读取了数据库的某一行,经过修改以后写回数据库,这时就遇到了问题。


    悲观锁

    悲观锁可以阻止一个事务以影响其他用户的方式来修改数据。如果一个事务执行的操作在某行数据应用了锁,那只有当这个事务把锁释放,其他事务才能够执行与该锁冲突的操作。悲观并发控制主要用于数据争用激烈的环境,以及发生并发冲突时使用锁保护数据的成本要低于回滚事务的成本的环境中。


    使用

    在对任意记录进行修改前,先尝试为该记录加上排他锁(exclusive locking)。

    如果加锁失败,说明该记录正在被修改,那么当前查询可能要等待或者抛出异常。 具体响应方式由开发者根据实际需要决定。

    如果成功加锁,那么就可以对记录做修改,事务完成后就会解锁了。

    其间如果有其他对该记录做修改或加排他锁的操作,都会等待我们解锁或直接抛出异常。


    优点与不足

    悲观并发控制实际上是“先取锁再访问”的保守策略,为数据处理的安全提供了保证。但是在效率方面,处理加锁的机制会让数据库产生额外的开销,还有增加产生死锁的机会;另外,在只读型事务处理中由于不会产生冲突,也没必要使用锁,这样做只能增加系统负载;还有会降低了并行性,一个事务如果锁定了某行数据,其他事务就必须等待该事务处理完才可以处理那行数。




0 0
原创粉丝点击