Mysql锁机制之乐观锁(Optimistic Locking)与悲观锁(exclusive locking)介绍

一.乐观锁与悲观锁

数据库管理系统 (DBMS) 中的并发控制的任务是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性

乐观并发控制 (乐观锁) 和悲观并发控制 (悲观锁) 是并发控制主要采用的技术手段。

无论是悲观锁还是乐观锁, 都是人们定义出来的概念, 可以认为是一种思想; 其实不仅仅是关系型数据库系统中有乐观锁和悲观锁的概念, 像memcache、hibernate、tair等都有类似的概念

针对于不同的业务场景, 应该选用不同的并发控制方式; 所以, 不要把乐观并发控制和悲观并发控制狭义的理解为DBMS中的概念, 更不要把他们和数据中提供的锁机制 (行锁、表锁、排他锁、共享锁) 混为一谈; 其实, 在DBMS中, 悲观锁正是利用数据库本身提供的锁机制来实现的

二.悲观锁

1.悲观锁介绍

悲观的认为操作数据库就是修改数据, 为了避免数据库中的数据同时被修改, 直接对该操作加锁处理

当我们要对一个数据库中的一条数据进行修改的时候, 为了避免同时被其他人修改, 最好的办法就是直接对该数据进行加锁以防止并发

这种借助数据库锁机制在修改数据之前先锁定, 再修改的方式被称之为悲观并发控制 (又名“悲观锁”,Pessimistic Concurrency Control, 缩写“PCC”)

  • 在关系数据库管理系统里,悲观并发控制 (又名“悲观锁”,Pessimistic Concurrency Control,缩写“PCC”) 是一种并发控制的方法

  • 它可以阻止一个事务以影响其他用户的方式来修改数据; 如果一个事务执行的操作都某行数据应用了锁,那只有当这个事务把锁释放, 其他事务才能够执行与该锁冲突的操作

  • 悲观并发控制主要用于数据争用激烈的环境, 以及发生并发冲突时使用锁保护数据的成本要低于回滚事务的成本的环境中

悲观锁, 正如其名, 它指的是对数据被外界 (包括本系统当前的其他事务, 以及来自外部系统的事务处理) 修改持保守态度(悲观), 因此, 在整个数据处理过程中, 将数据处于锁定状态;

悲观锁的实现, 往往依靠数据库提供的锁机制 (也只有数据库层提供的锁机制才能真正保证数据访问的排他性, 否则, 即使在本系统中实现了加锁机制, 也无法保证外部系统不会修改数据) , 现在互联网高并发的架构中, 受到 fail-fast 思路的影响, 悲观锁已经非常少见了

2.悲观锁的工作流程

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

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

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

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

ps : 行锁、表锁、读锁、写锁都是在操作之前先上排他锁

3.悲观锁总结

悲观并发控制主要用于数据争用激烈的环境, 以及发生并发冲突时使用锁保护数据的成本要低于回滚事务的成本的环境中

  • 优点

悲观并发控制实际上是“先取锁再访问”的保守策略, 为数据处理的安全提供了保证

  • 缺点
  • 在效率方面, 处理加锁的机制会让数据库产生额外的开销, 还有增加产生死锁的机会
  • 在只读型事务处理中由于不会产生冲突, 也没必要使用锁, 这样做只能增加系统负载
  • 会降低了并行性, 一个事务如果锁定了某行数据, 其他事务就必须等待该事务处理完才可以处理那行数

三.乐观锁

1.乐观锁介绍

乐观的认为操作数据库不会造成冲突, 只有在对数据进行更新的时候才会进行校验. 如果冲突就返回错误让用户决定如何去做

  • 在关系数据库管理系统里, 乐观并发控制 (又名“乐观锁”, Optimistic Concurrency Control, 缩写“OCC”) 是一种并发控制的方法
  • 它假设多用户并发的事务在处理时不会彼此互相影响, 各事务能够在不产生锁的情况下处理各自影响的那部分数据
  • 在提交数据更新之前, 每个事务会先检查在该事务读取数据后, 有没有其他事务又修改了该数据
  • 如果其他事务有更新的话, 正在提交的事务会进行回滚; 乐观事务控制最早是由孔祥重 (H.T.Kung) 教授提出

乐观锁 (Optimistic Locking) 相对悲观锁而言, 乐观锁假设认为数据一般情况下不会造成冲突, 所以在数据进行提交更新的时候, 才会正式对数据的冲突与否进行检测, 如果发现冲突了, 则让返回用户错误的信息, 让用户决定如何去做

相对于悲观锁, 在对数据库进行处理的时候, 乐观锁并不会使用数据库提供的锁机制; 一般的实现乐观锁的方式就是记录数据版本

数据版本 : 为数据增加的一个版本标识, 当读取数据时, 将版本标识的值一同读出, 数据每更新一次, 同时对版本标识进行更新

当我们提交更新的时候, 判断数据库表对应记录的当前版本信息与第一次取出来的版本标识进行比对, 如果数据库表当前版本号与第一次取出来的版本标识值相等, 则予以更新, 否则认为是过期数据

2.乐观锁的两种实现方式

  • 使用版本号实现

每一行数据多一个字段version, 每次更新数据对应版本号+1,
原理 : 读出数据, 将版本号一同读出, 之后更新, 版本号+1, 提交数据版本号大于数据库当前版本号, 则予以更新, 否则认为是过期数据, 重新读取数据

  • 使用时间戳实现

每一行数据多一个字段 time
原理 : 读出数据, 将时间戳一同读出, 之后更新, 提交数据时间戳等于数据库当前时间戳, 则予以更新, 否则认为是过期数据, 重新读取数据

3.乐观锁总结

乐观并发控制相信事务之间的数据竞争(data race)的概率是比较小的, 因此尽可能直接做下去, 直到提交的时候才去锁定, 所以不会产生任何锁和死锁

十二.悲观锁与乐观锁如何选择

两者的区别于使用场景 :

  • 乐观锁 : 乐观锁并未真正加锁, 效率高; 一旦锁的粒度掌握不好, 更新失败的概率就会比较高, 容易发生业务失败
  • 悲观锁 : 悲观锁依赖数据库锁, 效率低; 更新失败的概率比较低

随着互联网三高架构 (高并发、高性能、高可用) 的提出, 悲观锁已经越来越少的被使用到生产环境中了, 尤其是并发量比较大的业务场景

猜你喜欢

转载自blog.csdn.net/songhaixing2/article/details/114282041