相信所有后端选手每个人都听说过乐观锁和悲观锁吧,本文就详细说下乐观锁和悲观锁的区别。
乐观锁和悲观锁主要针对于先读后写的场景。
如果是全读的话,没必要加锁。
如果是全写的话,没必要用悲观锁。
我们拿账户扣钱的例子来说明一下:
//查询余额
select balance from deposit where id = xx;
//扣除余额
update deposit set balance -= 100 where id = xx;
系统首先查询出账户的余额,然后将余额减去100。
为什么要提前查一下呢?因为账户被封禁了,或者余额小于100等其他不合法状态 是 无法完成支付的。
上面这么写,明显是会出问题的。
假如当前账户余额还剩100,但是在查询和支付之间,当事人的老婆也花了100,这个时候update还是会成功执行的,余额就成了-100。
归根结底,select 和 update 之间有其他事物更改了数据。
乐观锁和悲观锁都能够解决这一问题,下面我们来讨论一下
//查询余额
select balance,version from deposit where id = xx;
//扣除余额
update deposit set balance -= 100 where id = xx and version = #{version};
乐观锁的处理方法如下 :
1、deposit表中,加入一个version列,用来标识数据的版本号,当事务每次更新数据的时候,都需要手动的将version+1。
2、select的时候将version也查询出来
3、update的时候,会判断记录当前的version是否等于select出来的version。
如果version一样,说明没有其他事务更改数据,可以更新成功
如果version不一样,说明有其他事务更改数据了,version!=/#{version},会导致update失败。需要进行自旋,重新执行查找和更新,直到更新成功。
乐观锁是一种CAS的思想。
//查询余额
select balance from deposit where id = xx for update;
//扣除余额
update deposit set balance -= 100 where id = xx;
悲观锁的做法是 在 select 的时候,就将对应的记录给加上排他锁,不允许其他事务执行当前读和写入。
这就保证了不可能有其他事务在select和update之间更新数据。
从上述流程可以看出,
1、乐观锁: 在select的时候不会加锁,这样做的好处是在select和update之间的时间段内,其他事务仍然可以执行当前读。
乐观锁适用于读多写少的场景,可以提高系统的吞吐量。
2、悲观锁: 在select的时候就会加锁,在select 和 update之间的时间段内,其他事务不能执行当前读。
悲观锁适用于写多读少的场景。
因为如果写多的话,乐观锁会有很大机率更新失败,需要自旋的不断执行查找和更新操作。自旋的时候会一直占用CPU,会耗费大量的CPU资源。
而悲观锁的话,当其他线程正在更新的时候,而拿不到锁的时候,会将线程挂起,交出CPU资源,可以把CPU给其他线程使用,提高了CPU的利用率。
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/qq_40276626/article/details/120794324
内容来源于网络,如有侵权,请联系作者删除!