MySQL之乐观锁和悲观锁详解

x33g5p2x  于2021-10-16 转载在 Mysql  
字(1.3k)|赞(0)|评价(0)|浏览(387)

相信所有后端选手每个人都听说过乐观锁和悲观锁吧,本文就详细说下乐观锁和悲观锁的区别。

场景

乐观锁和悲观锁主要针对于先读后写的场景。

如果是全读的话,没必要加锁。

如果是全写的话,没必要用悲观锁。

我们拿账户扣钱的例子来说明一下:

//查询余额
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的利用率。

相关文章

微信公众号

最新文章

更多