开发者社区> 牧小农> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

悲观锁与乐观锁的实现(详情图解)(2)

简介: 悲观锁与乐观锁的实现(详情图解)
+关注继续查看

三、锁的实现


悲观锁阻塞事务、乐观锁回滚重试:它们各有优缺点,不要认为一种一定好于另一种。像乐观锁适用于写比较少的情况下,即冲突真的很少发生的时候,这样可以省去锁的开销,加大了系统的整个吞吐量。但如果经常产生冲突,上层应用会不断的进行重试,这样反倒是降低了性能,所以这种情况下用悲观锁就比较合适。


3.1 悲观锁的实现方式


场景:


有用户A和用户B,在同一家店铺去购买同一个商品,但是商品的可购买数量只有一个

下面是这个店铺的商品表t_goods结构和表中的数据:

image.png


在不加锁的情况下,如果用户A和用户B同时下单,就会报错。


悲观锁的实现,往往依靠数据库提供的锁机制,在数据库中,我们如何用悲观锁去解决这个事情呢?


加入当用户A对下单购买商品(臭豆腐)的时候,先去尝试对该数据(臭豆腐)加上悲观锁

加锁失败:说明商品(臭豆腐)正在被其他事务进行修改,当前查询需要等待或者抛出异常,具体返回的方式需要由开发者根据具体情况去定义

加锁成功:对商品(臭豆腐)进行修改,也就是只有用户A能买,用户B想买(臭豆腐)就必须一直等待。当用户A买好后,用户B再想去买(臭豆腐)的时候会发现数量已经为0,那么B看到后就会放弃购买

在此期间如果有其他对该数据(臭豆腐)做修改或加锁的操作,都会等待我们解锁后或者直接抛出异常

image.png


那么如何加上悲观锁呢?我们可以通过以下语句给id=2的这行数据加上悲观锁,首先关闭MySQL数据库的自动提交属性。因为MySQL默认使用autocommit模式,也就是说,当我们执行一个更新操作后,MySQL会立刻将结果进行提交,(sql语句:set autocommit=0)


悲观锁加锁sql语句: select num from t_goods where id = 2 for update


我们通过开启mysql的两个会话,也就是两个命令行来演示:


事务A:

我们可以看到数据是立刻马上就可以查询出来,num=1

image.png


事务B:

我们是可以看到,事务B会一直等待事务A释放锁。如果事务A长期不释放锁,那么最终事务B将会报错,报错如下:Lock wait timeout exceeded; try restarting transaction,表示语句已被锁住

image.png


现在我们让事务A执行命令去修改数据,让臭豆腐的数量减一,然后查看修改后的数据,最后commit,结束事务

image.png



我们可以看到当我们事务A执行完成之后,臭豆腐的库存只有0个了,这个时候我们用户B再来购买这个臭豆腐的时候就会发现,最后一个臭豆腐已经被用户A购买完了,那么用户B只能放弃购买臭豆腐了。


通过悲观锁我们可以解决因为商品库存不足,导致的商品超出库存的售卖。


3.1 乐观锁的实现方式


对于上面的应用场景,我们应该怎么用乐观锁去解决呢?在上面的乐观锁中,我们有提到使用版本号(version)来解决,所以我们需要在t_goods加上版本号,调整后的sql表结构如下:

image.png


具体操作步骤如下:

1、首先用户A和用户B同时将臭豆腐(id=2)的数据查出来

2、然后用户A先买,用户A将(id=1和version=0)作为条件进行数据更新,将数量-1,并且将版本号+1。此时版本号变为1。用户A此时就完成了商品的购买

3、 用户B开始买,用户B也将(id=1和version=0)作为条件进行数据更新

4、更新完后,发现更新的数据行数为0,此时就说明已经有人改动过数据,此时就应该提示用户B重新查看最新数据购买


image.png


1、首先我们开启两个会话窗口,输入查询语句:select num from t_goods where id = 2

事务A:


image.png

事务B:

image.png


这个时候事务A和事务B同时获取相同的数据


2、此时事务A进行更新数据的操作,然后在查询更新后的数据

image.png

这个时候我们可以看到事务A更新成功,并且库存-1 版本号+1成功


2、此时事务B进行更新数据的操作,然后在查询更新后的数据

image.png

可以看到最终修改的时候失败,数据没有改变。此时就需要我们告知用户B重新处理


3.1.1 CAS


说到乐观锁,就必须提到一个概念:CAS

什么是CAS呢?Compare-and-Swap,即比较并替换,也有叫做Compare-and-Set的,比较并设置。

1、比较:读取到了一个值A,在将其更新为B之前,检查原值是否仍为A(未被其他线程改动)。

2、设置:如果是,将A更新为B,结束。[1]如果不是,则什么都不做。

上面的两步操作是原子性的,可以简单地理解为瞬间完成,在CPU看来就是一步操作。

有了CAS,就可以实现一个乐观锁,允许多个线程同时读取(因为根本没有加锁操作),但是只有一个线程可以成功更新数据,并导致其他要更新数据的线程回滚重试。 CAS利用CPU指令,从硬件层面保证了操作的原子性,以达到类似于锁的效果。


Java中真正的CAS操作调用的native方法

因为整个过程中并没有“加锁”和“解锁”操作,因此乐观锁策略也被称为无锁编程。换句话说,乐观锁其实不是“锁”,它仅仅是一个循环重试CAS的算法而已,但是CAS有一个问题那就是会产生ABA问题,什么是ABA问题,以及如何解决呢?


ABA 问题:

如果一个变量V初次读取的时候是A值,并且在准备赋值的时候检查到它仍然是A值,那我们就能说明它的值没有被其他线程修改过了吗?很明显是不能的,因为在这段时间它的值可能被改为其他值,然后又改回A,那CAS操作就会误认为它从来没有被修改过。这个问题被称为CAS操作的 "ABA"问题。


ABA 问题解决:

我们需要加上一个版本号(Version),在每次提交的时候将版本号+1操作,那么下个线程去提交修改的时候,会带上版本号去判断,如果版本修改了,那么线程重试或者提示错误信息~


四、如何选择


悲观锁阻塞事务,乐观锁回滚重试,它们各有优缺点,不要认为一种一定好于另一种。像乐观锁适用于写比较少的情况下,即冲突真的很少发生的时候,这样可以省去锁的开销,加大了系统的整个吞吐量。


但如果经常产生冲突,上层应用会不断的进行重试,这样反倒是降低了性能,所以这种情况下用悲观锁就比较合适。


注意点:


1、乐观锁并未真正加锁,所以效率高。一旦锁的粒度掌握不好,更新失败的概率就会比较高,容易发生业务失败。


2、悲观锁依赖数据库锁,效率低。更新失败的概率比较低。


五、总结


这篇文章讲解了悲观锁与乐观锁的区别,以及实现场景,不管是悲观锁还是乐观锁都是人们定义出来的概念,是一种思想,如何有有疑问或者问题的小伙伴可以在下面进行留言,小农看到了会第一时间回复大家,谢谢,大家加油~



版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
NUCLEO-L432KC实现UART1、UART2双串口数据通信(STM32L432KC)
NUCLEO-L432KC实现UART1、UART2双串口数据通信(STM32L432KC)
77 0
《Linux内核设计的艺术:图解Linux操作系统架构设计与实现原理》——3.2 内核第一次做进程调度
本节书摘来自华章计算机《Linux内核设计的艺术:图解Linux操作系统架构设计与实现原理》一书中的第3章,第3.2节,作者:新设计团队著, 更多章节内容可以访问云栖社区“华章计算机”公众号查看。
1152 0
《Linux内核设计的艺术:图解Linux操作系统架构设计与实现原理》——第3章 进程1的创建及执行 3.1 进程1的创建
本节书摘来自华章计算机《Linux内核设计的艺术:图解Linux操作系统架构设计与实现原理》一书中的第3章,第3.1节,作者:新设计团队著, 更多章节内容可以访问云栖社区“华章计算机”公众号查看。
1269 0
《Linux内核设计的艺术:图解Linux操作系统架构设计与实现原理》——2.13 开启中断
本节书摘来自华章计算机《Linux内核设计的艺术:图解Linux操作系统架构设计与实现原理》一书中的第2章,第2.13节,作者:新设计团队著, 更多章节内容可以访问云栖社区“华章计算机”公众号查看。
946 0
《Linux内核设计的艺术:图解Linux操作系统架构设计与实现原理》——1.1 启动BIOS,准备实模式下的中断向量表和中断服务程序
本节书摘来自华章计算机《Linux内核设计的艺术:图解Linux操作系统架构设计与实现原理》一书中的第1章,第1.1节,作者:新设计团队著, 更多章节内容可以访问云栖社区“华章计算机”公众号查看。
1501 0
《Linux内核设计的艺术:图解Linux操作系统架构设计与实现原理》——1.2 加载操作系统内核程序并为保护模式做准备
本节书摘来自华章计算机《Linux内核设计的艺术:图解Linux操作系统架构设计与实现原理》一书中的第1章,第1.2节,作者:新设计团队著, 更多章节内容可以访问云栖社区“华章计算机”公众号查看。
1739 0
+关注
牧小农
业精于勤荒于嬉,行成于思毁于随。
134
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载