lock是悲观锁还是乐观锁_图文并茂的带你彻底理解悲观锁与乐观锁
点击上方蓝色字体,选择“设置星标”
优质文章,第一时间送达
文章转自:Hollis
原创:安静的boy
这是一篇介绍悲观锁和乐观锁的入门文章。旨在让那些不了解悲观锁和乐观锁的小白们弄清楚什么是悲观锁,什么是乐观锁。不同于其他文章,本文会配上相应的图解让大家更容易理解。通过该文,你会学习到如下的知识。
1
锁(Lock)
在介绍悲观锁和乐观锁之前,让我们看一下什么是锁。
锁,在我们生活中随处可见,我们的门上有锁,我们存钱的保险柜上有锁,是用来保护我们财产安全的。
程序中也有锁,当多个线程修改共享变量时,我们可以给修改操作上锁(syncronized)。
当多个用户修改表中同一数据时,我们可以给该行数据上锁(行锁)。因此,锁其实是在并发下控制多个操作的顺序执行,以此来保证数据安全的变动。
并且,锁是一种保证数据安全的机制和手段,而并不是特定于某项技术的。悲观锁和乐观锁亦是如此。本篇介绍的悲观锁和乐观锁是基于数据库层面的。
2
悲观锁
悲观锁(Pessimistic Concurrency Control),第一眼看到它,相信每个人都会想到这是一个悲观的锁。没错,它就是一个悲观的锁。
那这个悲观体现在什么地方呢?悲观是我们人类一种消极的情绪,对应到锁的悲观情绪,悲观锁认为被它保护的数据是极其不安全的,每时每刻都有可能变动,一个事务拿到悲观锁后(可以理解为一个用户),其他任何事务都不能对该数据进行修改,只能等待锁被释放才可以执行。
数据库中的行锁,表锁,读锁,写锁,以及syncronized实现的锁均为悲观锁。
这里再介绍一下什么是数据库的表锁和行锁,以免有的同学对后面悲观锁的实现看不明白。
我们经常使用的数据库是mysql,mysql中最常用的引擎是Innodb,Innodb默认使用的是行锁。而行锁是基于索引的,因此要想加上行锁,在加锁时必须命中索引,否则将使用表锁。
3
乐观锁
与悲观相对应,乐观是我们人类一种积极的情绪。乐观锁(Optimistic Concurrency Control)的“乐观情绪”体现在,它认为数据的变动不会太频繁。因此,它允许多个事务同时对数据进行变动。
但是,乐观不代表不负责,那么怎么去负责多个事务顺序对数据进行修改呢?
乐观锁通常是通过在表中增加一个版本(version)或时间戳(timestamp)来实现,其中,版本最为常用。
事务在从数据库中取数据时,会将该数据的版本也取出来(v1),当事务对数据变动完毕想要将其更新到表中时,会将之前取出的版本v1与数据中最新的版本v2相对比,如果v1=v2,那么说明在数据变动期间,没有其他事务对数据进行修改,此时,就允许事务对表中的数据进行修改,并且修改时version会加1,以此来表明数据已被变动。
如果,v1不等于v2,那么说明数据变动期间,数据被其他事务改动了,此时不允许数据更新到表中,一般的处理办法是通知用户让其重新操作。不同于悲观锁,乐观锁是人为控制的。
4
如何实现
经过上面的学习,我们知道悲观锁和乐观锁是用来控制并发下数据的顺序变动问题的。那么我们就模拟一个需要加锁的场景,来看不加锁会出什么问题,并且怎么利用悲观锁和乐观锁去解决。
场景:A和B用户最近都想吃猪肉脯,于是他们打开了购物网站,并且找到了同一家卖猪肉脯的>店铺。下面是这个店铺的商品表goods结构和表中的数据。
id |
name |
num |
1 |
猪肉脯 |
1 |
2 |
牛肉干 |
1 |
从表中可以看到猪肉脯目前的数量只有1个了。在不加锁的情况下,如果A,B同时下单,就有可能导致超卖。
悲观锁解决
利用悲观锁的解决思路是,我们认为数据修改产生冲突的概率比较大,所以在更新之前,我们显示的对要修改的记录进行加锁,直到自己修改完再释放锁。加锁期间只有自己可以进行读写,其他事务只能读不能写。
A下单前先给猪肉脯这行数据(id=1)加上悲观锁(行锁)。此时这行数据只能A来操作,也就是只有A能买。B想买就必须一直等待。
当A买好后,B再想去买的时候会发现数量已经为0,那么B看到后就会放弃购买。
那么如何给猪肉脯也就是id=1这条数据加上悲观锁锁呢?我们可以通过以下语句给id=1的这行数据加上悲观锁
select num from goods where id = 1 for update;
下面是悲观锁的加锁图解
我们通过开启mysql的两个会话,也就是两个命令行来演示。
1、事务A执行命令给id=1的数据上悲观锁准备更新数据
这里之所以要以begin开始,是因为mysql是自提交的,所以要以begin开启事务,否则所有修改将被mysql自动提交。
2、事务B也去给id=1的数据上悲观锁准备更新数据
我们可以看到此时事务B再一直等待A释放锁。如果A长期不释放锁,那么最终事务B将会报错,这有兴趣的可以去尝试一下。
3、接着我们让事务A执行命令去修改数据,让猪肉脯的数量减一,然后查看修改后的数据,最后commit,结束事务。
我们可以看到,此时最后一个猪肉脯被A买走,只剩0个了。
4、当事务A执行完第3步后,我们看事务B中出现了什么
我们看到由于事务A释放了锁,事务B就结束了等待,拿到了锁,但是数据此时变成了0,那么B看到后就知道被买走了,就会放弃购买。
通过悲观锁,我们解决了猪肉脯购买的问题。
乐观锁解决
下面,我们利用乐观锁来解决该问题。上面乐观锁的介绍中,我们提到了,乐观锁是通过版本号version来实现的。所以,我们需要给goods表加上version字段,表变动后的结构如下:
id |
name |
num |
version |
1 |
猪肉脯 |
1 |
0 |
1 |
牛肉干 |
1 |
0 |
使用乐观锁的解决思路是,我们认为数据修改产生冲突的概率并不大,多个事务在修改数据的之前先查出版本号,在修改时把当前版本号作为修改条件,只会有一个事务可以修改成功,其他事务则会失败。
A和B同时将猪肉脯(id=1下面都说是id=1)的数据查出来,然后A先买,A将id=1和version=0作为条件进行数据更新,即将数量-1,并且将版本号+1。
此时版本号变为1。A此时就完成了商品的购买。最后B开始买,B也将id=1和version=0作为条件进行数据更新,但是更新完后,发现更新的数据行数为0,此时就说明已经有人改动过数据,此时就应该提示用户重新查看最新数据购买。
下面是乐观锁的加锁图解
我们还是通过开启mysql的两个会话,也就是两个命令行来演示。
1、事务A执行查询命令,事务B执行查询命令,因为两者查询的结果相同,所以下面我只列出一个截图。
此时A和B均获取到相同的数据
2、事务A进行购买更新数据,然后再查询更新后的数据。
我们可以看到事务A成功更新了数据和版本号。
事务B再进行购买更新数据,然后我们看影响行数和更新后的数据
可以看到最终修改行数为0,数据没有改变。此时就需要我们告知用户重新处理。
5
优缺点
下面我们介绍下乐观锁和悲观锁的优缺点以便我们分析他们的应用场景,这里我只分析最重要的优缺点,也是我们要记住的。
悲观锁
优点:悲观锁利用数据库中的锁机制来实现数据变化的顺序执行,这是最有效的办法
缺点:一个事务用悲观锁对数据加锁之后,其他事务将不能对加锁的数据进行除了查询以外的所有操作,如果该事务执行时间很长,那么其他事务将一直等待,那势必影响我们系统的吞吐量。
乐观锁
优点:乐观锁不在数据库上加锁,任何事务都可以对数据进行操作,在更新时才进行校验,这样就避免了悲观锁造成的吞吐量下降的劣势。
缺点:乐观锁因为是通过我们人为实现的,它仅仅适用于我们自己业务中,如果有外来事务插入,那么就可能发生错误。
6
应用场景
悲观锁:因为悲观锁会影响系统吞吐的性能,所以适合应用在写为居多的场景下。
乐观锁:因为乐观锁就是为了避免悲观锁的弊端出现的,所以适合应用在读为居多的场景下。
参考资料
https://chenzhou123520.iteye.com/blog/1860954
https://baike.baidu.com/item/乐观锁/7146502
遗漏热文?赶紧标星
1. 阿里社招面试指南
2. 阿里应届生面试指南
3. 探寻线程池是如何工作的
4. 到底线程池应该设置多少合适?
5. 跳槽的必备条件是有一份好的简历
6. 不是所有的 Github 都适合写在简历上
7. 所没有项目经验找工作处处碰壁怎么办
8. 每一个开发人员都应该懂得的 UML 规范
9. 工作环境没机会接触高并发、分布式怎么办?
10. 这算是有史以来讲数据库连接池数最清楚的文章了
11. 你以为认为 count(1) 比 count(*) 效率高么?
12. 用了这么多年 Spring Boot 你知道他爹有多大背景吗?
lock是悲观锁还是乐观锁_图文并茂的带你彻底理解悲观锁与乐观锁相关推荐
- 图文并茂的带你彻底理解悲观锁与乐观锁
这是一篇介绍悲观锁和乐观锁的入门文章.旨在让那些不了解悲观锁和乐观锁的小白们弄清楚什么是悲观锁,什么是乐观锁.不同于其他文章,本文会配上相应的图解让大家更容易理解.通过该文,你会学习到如下的知识. 1 ...
- linux telnet成功显示什么_一文带你彻底理解 Linux 的各种终端类型及概念
(给Linux爱好者加星标,提升Linux技能) 转自:dog250 https://blog.csdn.net/dog250/article/details/78766716 每天使用Linux每天 ...
- 画出该lti系统的幅频特性响应曲线_一文带你通俗理解幅频响应和相频响应
引言 好多人学习数字信号处理学了很久都没有明白这两个概念的真实含义,或者说很多人在设计滤波器的时候,根本就没有考虑什么是幅频响应和相频响应.只是一昧地把滤波器设计出来然后把不要的频率滤除掉,这样是要 ...
- 两个相邻盒子的边框怎么只显示一个_一篇文章带你快速理解盒子模型「经典案例」...
今天带大家快速理解盒子模型,直接上代码: css盒子 我的css盒子测试模型 上面代码没有任何难度,只是写了一个div标签,大家已经知道,div标签是块级元素,所以会占满一行: 但是我们也注意到了图片 ...
- 代码的c++实现_一文带你快速理解FreeRTOS代码规范~
[导读] 遇到些朋友感觉FreeRTOS内核代码看起来很不习惯,不习惯其编码风格,本文就来梳理一下其代码规范,便于提高阅读其代码的效率.代码基于FreeRTOS V10.4.3. FreeRTOS代码 ...
- 带通 带阻滤波器 幅频响应_一文带你通俗理解幅频响应和相频响应
引言 好多人学习数字信号处理学了很久都没有明白这两个概念的真实含义,或者说很多人在设计滤波器的时候,根本就没有考虑什么是幅频响应和相频响应.只是一昧地把滤波器设计出来然后把不要的频率滤除掉,这样是要吃 ...
- mysql锁的应用场景_浅谈Mysql共享锁、排他锁、悲观锁、乐观锁及其使用场景
Mysql共享锁.排他锁.悲观锁.乐观锁及其使用场景 一.相关名词 |--表级锁(锁定整个表) |--页级锁(锁定一页) |--行级锁(锁定一行) |--共享锁(S锁,MyISAM 叫做读锁) |-- ...
- mysql悲观锁和乐观优缺点_乐观锁、悲观锁和MVCC各是什么?各自优缺点是什么?...
在数据库的实际使用过程中,我们常常会遇到不希望数据被同时写或者读的情景,例如秒杀场景下,两个请求同时读到系统还有库存1个,然后又先后把库存更新为0,这时候就会出现超卖的情况,这时候货物的实际库存和我们 ...
- **Java有哪些悲观锁的实现_「Java并发编程」何谓悲观锁与乐观锁,Java编程你会吗...
何谓悲观锁与乐观锁 悲观锁 乐观锁 两种锁的使用场景 乐观锁常见的两种实现方式 1. 版本号机制 2. CAS算法 乐观锁的缺点 1 ABA 问题 2 循环时间长开销大 3 只能保证一个共享变量的原子 ...
最新文章
- 《概率机器人》里程计运动模型gmapping中代码解析
- 搜索4--noi6264:走出迷宫
- 小程序短视频项目———开发用户登录注册(一)
- php mysql_fetch_array mysql_fetch__php提示Warning:mysql_fetch_array() expects的解决方法
- servlet-新建maven报错:web.xml is missing and <failOnMissingWebXml> is set to true
- 云耀服务器切换系统,【计算】云耀服务器-常见操作汇总指南
- 一种改进的高光谱图像CEM目标检测算法
- 打基础一定要吃透这12类 Python 内置函数
- Rock8247 bsp-Tornado-VXWorks Build up
- IntelliJ IDEA 2018.2.2远程调试Tomcat的配置方法
- oracle 12c 取消omf,Oracle12c创建及删除PDB
- jfinal使用配置文件注意事情
- JAVA集合系列(4):关于数组复制的几种方法(源码解读)及执行效率,我们知多少?
- linux火狐怎么清除缓存文件,火狐浏览器如何设置_火狐浏览器怎样清除缓存
- [资源帖]ICML2016 下载地址
- 微信大改版,时隔4年再次华丽转身。
- 量子计算机意识永生,量子计算机如何使未来的人类永生
- Matomo API 官方接口详情
- C#练习之打印出所有水仙花数
- 三国群英传霸业之王服务器维护,20200901维护公告
热门文章
- 找出js里面改变cookies的代码
- android listview item 展开动画,android的ListView点击item使item展开的做法的实现代码
- 小妙招:如何防止你的 jar 包被反编译?
- 从Nacos客户端视角来分析一下配置中心实现原理
- RocketMQ入门到入土(七 )为什么同一个消费组设置不同tag会出现奇怪现象
- 结构型设计模式在公司项目中的运用实践
- Flask自定义错误页面的方法
- 大话数据结构 -07-1 图的定义、抽象数据类型与存储结构
- OUTLOOK2019 解决 无法验证您连接到的服务器使用的安全证书
- YUV422(UYVY)转RGB565源代码及其讲解.md