【MySql】悲观锁和乐观锁的介绍

一、并发控制

当程序中可能出现并发的情况时,就需要保证在并发情况下数据的准确性,以此确保当前用户和其他用户一起操作时,所得到的结果和他单独操作时的结果是一样的。这就叫做并发控制。并发控制的目的是保证一个用户的工作不会对另一个用户的工作产生不合理的影响。

没有做好并发控制,就可能导致脏读、幻读和不可重复读等问题。

实现并发控制的主要手段分为乐观并发控制和悲观并发控制两种。

无论是悲观锁还是乐观锁,都是人们定义出来的概念,可以认为是一种思想。乐观锁比较适用于读多写少的情况(多读场景),悲观锁比较适用于写多读少的情况(多写场景)。

二、案例说明

银行两操作员(甲/乙)同时操作同一账户。两人同时读取一余额为 1000 元的账户,甲为该账户增加 100 元,乙同时为该账户扣除 50 元,甲先提交,乙后提交。最后实际账户余额为 1000-50=950,但本该为 1000+100-50=1050。这就是典型的并发问题。

解释造成这样的原因:

甲和乙同时读取余额为1000,甲先增加100元,更新余额为1100,甲先提交。此时乙读取的余额还是原来的1000,乙扣除50元,更新余额为950,最后实际账户余额显示950。

三、悲观锁(Pessimistic lock)

1、理解

当要对数据库中的一条数据进行修改的时候,为了避免同时被其他人修改,最好的办法就是直接对该数据进行加锁以防止并发。这种借助数据库锁机制,在修改数据之前先锁定,再修改的方式被称之为悲观并发控制【Pessimistic Concurrency Control,缩写“PCC”,又名“悲观锁”】。

悲观锁,具有强烈的独占和排它特性。它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度。因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。

之所以叫做悲观锁,是因为这是一种对数据的修改持有悲观态度的并发控制方式。总是假设最坏的情况,每次读取数据的时候都默认其他线程会更改数据,因此需要进行加锁操作,当其他线程想要访问数据时,都需要阻塞挂起。悲观锁的实现:

  1. 传统的关系型数据库使用这种锁机制,比如行锁、表锁、读锁、写锁等,都是在操作之前先上锁。
  2. Java 里面的同步 synchorized 关键字的实现。

2、悲观锁主要分为共享锁和排他锁

  • 共享锁【shared locks】又称为读锁,简称 S 锁。顾名思义,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改。
  • 排他锁【exclusive locks】又称为写锁,简称 X 锁。顾名思义,排他锁就是不能与其他锁并存,如果一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁。获取排他锁的事务可以对数据行读取和修改。

3、说明

悲观并发控制实际上是“先取锁再访问”的保守策略,为数据处理的安全提供了保证。但是在效率方面,处理加锁的机制会让数据库产生额外的开销,还有增加产生死锁的机会。另外还会降低并行性,一个事务如果锁定了某行数据,其他事务就必须等待该事务处理完才可以处理那行数据。

四、乐观锁(Optimistic lock)

1、理解

乐观锁是相对悲观锁而言的,乐观锁假设数据一般情况不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果冲突,则返回给用户异常信息,让用户决定如何去做。乐观锁适用于读多写少的场景,这样可以提高程序的吞吐量。

乐观锁采取了更加宽松的加锁机制。也是为了避免数据库幻读、业务处理时间过长等原因引起数据处理错误的一种机制,但乐观锁不会刻意使用数据库本身的锁机制,而是依据数据本身来保证数据的正确性。乐观锁的实现: 

  • CAS 实现:Java 中java.util.concurrent.atomic包下面的原子变量使用了乐观锁的一种 CAS 实现方式。
  • 版本号控制:一般是在数据表中加上一个数据版本号 version 字段,表示数据被修改的次数。当数据被修改时,version 值会 +1。当线程 A 要更新数据时,在读取数据的同时也会读取 version 值,在提交更新时,若刚才读取到的 version 值与当前数据库中的 version 值相等时才更新,否则重试更新操作,直到更新成功。

2、说明

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

五、具体实现

1、悲观锁实现方式

悲观锁的实现,往往依靠数据数据库提供的锁机制。在数据库中,悲观锁的流程如下:

  1. 在对记录进行修改前,先尝试为该记录加上排他锁(exclusive locks)。
  2. 如果加锁失败,说明该记录正在被修改,那么当前查询可能要等待或者抛出异常。具体响应方式由开发者根据实际需要决定。
  3. 如果成功加锁,那么就可以对记录做修改,事务完成后就会解锁了。
  4. 期间如果有其他对该记录做修改或加排他锁的操作,都会等待解锁或直接抛出异常。

以 MySql Innodb 引擎举例,说明 SQL 中悲观锁的应用

要使用悲观锁,必须关闭 MySql 数据库的自动提交属性 set autocommit = 0。因为 MySql 默认使用 autocommit 模式,也就是说,当执行一个更新操作后,MySQL 会立刻将结果进行提交。

以电商下单扣减库存的过程说明一下悲观锁的使用:

// 0、开始事务
begin
// 1、查询出商品库存信息
select quantity from items where id = 1 for update;
// 2、修改库存商品为2
update items set quantity = 2 where id = 1
// 3、提交事务
commit

在对 id = 1 的记录修改前,先通过 for update 的方式进行加锁,然后再进行修改。这就是比较典型的悲观锁策略。 

如果发生并发,同一时间只有一个线程可以开启事务并获得 id=1 的锁,其它的事务必须等本次事务提交之后才能执行。这样可以保证当前的数据不会被其它事务修改。

使用 select ... for update 锁数据,需要注意锁的级别,MySQL InnoDB 默认行级锁。行级锁都是基于索引的,如果一条 SQL 语句用不到索引是不会使用行级锁的,会使用表级锁把整张表锁住,这点需要注意。

2、乐观锁实现方式  (乐观锁不需要借助数据库的锁机制)

当要更新一条记录的时候,希望这条记录没有被别人更新

乐观锁实现方式:

  1. 取出记录时,获取当前 version
  2. 更新时,带上这个 version
  3. 执行更新时, set version = newVersion where version = oldVersion
  4. 如果 version 不对,就更新失败

主要就是两个步骤:冲突检测和数据更新。

比如前面的扣减库存问题,通过乐观锁可以实现如下:

// 1、查询出商品库存信息,quantity = 3
select quantity from items where id = 1;

// 2、修改商品库存为 2
update items set quantity = 2 where id = 1 and quantity = 3

在更新之前,先查询一下库存表中当前库存数(quantity),然后在做 update 的时候,以库存数作为一个修改条件。当提交更新的时候,判断数据库表对应记录的当前库存数与第一次取出来的库存数进行比对,如果数据库表当前库存数与第一次取出来的库存数相等,则予以更新,否则认为是过期数据。 

以上更新语句存在一个比较严重的问题,即 ABA 问题:

 

  1. 比如说线程1从数据库取出库存数为3,这时候线程2也从数据库中取出库存数3,并且线程2进行了一些操作变成了2。
  2. 然后线程2又将库存数变成3,这时候线程1进行CAS操作发现数据库中仍然是3,然后线程1操作成功。
  3. 尽管线程1 的CAS 操作成功,但是不代表这个过程就是没有问题的。

一个比较好的解决办法,就是通过一个单独的可以顺序递增的 version 字段。优化如下: 

乐观锁每次在执行数据修改操作时,都会带上一个版本号,一旦版本号和数据的版本号一致就可以执行修改操作并对版本号执行 +1 操作,否则就执行失败。因为每次操作的版本号都会随之增加,所以不会出现 ABA 问题。除了 version 以外,还可以使用时间戳,因为时间戳天然具有顺序递增性。

以上 SQL 其实还是有一定的问题的,就是一旦遇上高并发的时候,就只有一个线程可以修改成功,那么就会存在大量的失败。对于像淘宝这样的电商网站,高并发是常有的事,总让用户感知到失败显然是不合理的。所以,还是要想办法减少乐观锁的粒度。一个比较好的建议,就是减小乐观锁力度,最大程度的提升吞吐率,提高并发能力!如下:

// 修改商品库存
update items set quantity = quantity - 1 where id = 1 and quantity -1 > 0

以上 SQL 语句中,如果用户下单数为 1,则通过 quantity -1 > 0 的方式进行乐观锁控制。在执行过程中,会在一次原子操作中查询一遍 quantity 的值,并将其扣减掉 1。 

高并发环境下锁粒度把控是一门重要的学问。选择一个好的锁,在保证数据安全的情况下,可以大大提升吞吐率,进而提升性能。 

六、参考文档

MySql悲观锁(行锁)和乐观锁

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/196280.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据安全:专业服务与您共同对抗.faust数字勒索的威胁

引言: 在数字世界的幕后,一股黑暗势力悄然崛起。.faust勒索病毒,如同数码时代的黑手党,通过其高度精密的加密技术,正在肆虐用户和组织的数据。本文将深入挖掘.faust的狡猾手法,为您揭示其隐藏在数字背后的…

居家适老化设计第三十三条---卫生间之暖风

居家适老化是指为了满足老年人居住需求而进行的住房改造,以提供更加安全、舒适、便利的居住环境。在居家适老化中,暖风系统是一个重要的考虑因素。暖风系统可以提供温暖舒适的室内温度,对老年人来说尤为重要。老年人常常身体机能下降&#xf…

浅谈基于EIoT能源物联网的工厂智能照明系统应用改造

【摘要】:随着物联网技术的发展,许多场所针对照明合理应用物联网照明系统,照明作为工厂的重要能耗之一,工厂的照明智能化控制,如何优化控制、提高能源的利用率,达到节约能源的目的。将互联网的技术应用到工…

PPSSPP (PSP游戏模拟器)最新版安装使用教程

PPSSPP优势 1、目前唯一的也是最好的psp模拟器 可运行绝大多数psp游戏且运行高速,即使是低配手机也能游玩经典大作。 2、支持自定义调节虚拟手柄和实体手柄连接 ppsspp模拟器支持使用虚拟手柄或者连接实体手柄游玩,同时还可以自定义调节按键选项。 …

mac电脑下载Netflix Mac(奈飞客户端)安装教程

Netflix Mac,奈飞官方客户端,带给您无限的电影和剧集体验!与朋友分享最新热门剧集、电影,与家人一起享受高品质的流媒体内容。 通过Netflix Mac,您可以轻松地搜索、浏览和观看各种类型的影片,包括剧情片、…

Leetcode刷题之设计循环队列(C语言版)

Leetcode刷题之设计循环队列(C语言版) 一、题目描述二、题目示例三、题目解析Ⅰ、typedef structⅡ、MyCircularQueue* myCircularQueueCreate(int k)Ⅲ、bool myCircularQueueIsEmpty(MyCircularQueue* obj)Ⅳ、bool myCircularQueueIsFull(MyCircularQ…

老师怎样处理校园欺凌

校园欺凌是一个让人痛心又不可忽视的问题。作为老师,该如何处理这种问题,既能够保护受欺凌的学生,又能够让施暴者得到应有的教训呢? 及时发现并介入 经常关注学生的动态,一旦发现有校园欺凌的苗头,就要及时…

如何轻松将 4K 转换为 1080p 高清视频

由于某些原因,你可能有一些 4K 视频,与1080p、1080i、720p、720i等高清视频相比,4K 视频具有更高的分辨率,可以给您带来更多的视觉和听觉享受。但是,播放4k 视频是不太容易的,因为超高清电视没有高清电视那…

医疗器械企业升级路:直连客户盘活存量,布局出海寻求增量

随着随着医疗各领域VBP(带量采购)的稳步推进以及医疗机构DRG/DIP(按疾病诊断相关分组/病种分值支付)的深化应用,降本增效和精细化管理已经成为医院管理者的头等大事。 这也在倒逼医疗器械厂商提升管理水平和营销效率。…

FFA 2023|字节跳动 7 项议题入选

Flink Forward 是由 Apache 官方授权的 Apache Flink 社区官方技术大会,作为最受 Apache Flink 社区开发者期盼的年度峰会之一,FFA 2023 将持续集结行业最佳实践以及 Flink 最新技术动态,是中国 Flink 开发者和使用者不可错过的的技术盛宴。 …

竞赛选题 题目:基于机器视觉的图像矫正 (以车牌识别为例) - 图像畸变校正

文章目录 0 简介1 思路简介1.1 车牌定位1.2 畸变校正 2 代码实现2.1 车牌定位2.1.1 通过颜色特征选定可疑区域2.1.2 寻找车牌外围轮廓2.1.3 车牌区域定位 2.2 畸变校正2.2.1 畸变后车牌顶点定位2.2.2 校正 7 最后 0 简介 🔥 优质竞赛项目系列,今天要分享…

视频文案怎么写,媒介盒子支招

近几年短视频成为风口,各行各业都想分一杯羹,但是一头热的你,是否知道短视频的相关文案怎么写呢?正所谓兵马未动,文案先行,一个合适的文案是上热门的秘密武器,今天媒介盒子就来和大家聊聊:视频…

概要设计检查单、需求规格说明检查单

1、概要设计检查表 2、需求规格说明书检查表 概要(结构)设计检查表 工程名称 业主单位 承建单位 检查依据 1、设计方案、投标文件;2、合同;3、信息系统相关技术标准及安全规范; 检查类目 检查内容 检查…

汽车电子 -- 车载ADAS之RCW(后碰撞预警系统)

相关法规文件: RCW: GB 4785-2019 汽车及挂车外部照明和光信号装置的安装规定 一、后方碰撞预警系统 RCW( Rear Collision Warning ) 参看:功能定义-后方碰撞预警 RCW 功能可以对自车行驶过程中对后方车辆进行监测&#xff0…

Tableau连接到mysql数据库,配置驱动

Tableau想要连接mysql数据库进行数据的可视化,但是没有ODBC驱动,看了几篇文章写的,不是很清楚,顺便写下自己的思路。 1、下载mysql对应的ODBC驱动 首先要知道自己mysql的版本,然后下载对应的ODBC驱动。 MySQL :: Dow…

colab notebook导出为PDF

目录 方法一:使用浏览器打印功能 方法二:使用nbconvert转换 方法三:在线转换 方法一:使用浏览器打印功能 一般快捷键是CTRLP 然后改变目标打印机为另存为PDF 这样就可以将notebook保存为PDF了 方法二:使用nbconver…

供应链攻击的类型和预防

供应链攻击是一种面向软件开发人员和供应商的新兴威胁,目标是通过感染合法应用分发恶意软件来访问源代码、构建过程或更新机制。 供应链攻击是威胁行为者通过利用软件供应链中的漏洞进入组织网络的一种网络攻击,供应链攻击的目标可以是软件开发过程中的…

虚幻学习笔记5—UI预设体制作

一、前言 本文使用的虚幻引擎5.3.2,在unity中有预设体的概念,可以将一个组合型的物体或UI制作成预设体,方便后续可以快速制作更多元的内容和复用。虚幻本身没有这个概念,但是要实现类似的效果其,故此我引用了这个概念。…

【密码学引论】Hash密码

第六章 Hash密码 md4、md5、sha系列、SM3 定义:将任意长度的消息映射成固定长度消息的函数功能:确保数据的真实性和完整性,主要用于认证和数字签名Hash函数的安全性:单向性、抗若碰撞性、抗强碰撞性生日攻击:对于生日…

这才是BI大数据分析工具的正确打开方式!

这两年经济下行给各行各业造成不小的发展困扰,为此企业积极自救,希望通过数字化降本增效,提高业绩水平。BI大数据分析工具就是企业数字化转型中常用到的一种商业智能BI工具,主要作用是缩短数据分析时间,提升企业数据分…