求你了,别在高并发场景中使用悲观锁了!

共 2269字,需浏览 5分钟

 ·

2022-06-25 08:21

△Hollis, 一个对Coding有着独特追求的人△

作者 l Hollis
来源 l Hollis(ID:hollischuang)

Hollis的新书限时折扣中,一本深入讲解Java基础的干货笔记!

我们知道,乐观锁和悲观锁是并发控制主要采用的技术手段,通常用在数据库管理中。

但是,乐观锁、悲观锁并不像行级锁、共享锁等概念一样是真实存在的锁。其实他们只是人们定义出来的概念,可以认为是一种思想。 其实不仅仅是关系型数据库系统中有乐观锁和悲观锁的概念,像memcache、hibernate、tair等都有类似的概念。

针对于不同的业务场景,应该选用不同的并发控制方式。所以,不要把乐观并发控制和悲观并发控制狭义的理解为DBMS中的概念,更不要把他们和数据中提供的锁机制(行锁、表锁、排他锁、共享锁)混为一谈。其实,在DBMS中,悲观锁正是利用数据库本身提供的锁机制来实现的。

网上有很多关于乐观锁和悲观锁的介绍,我之前也有文章(《深入理解乐观锁与悲观锁》)专门介绍过,这里为了方便大家理解,就简单做个总结。



悲观锁和乐观锁

悲观锁,正如其名,它指的是对数据被外界修改持悲观态度,因此,在整个数据处理过程中,需要先将数据进行锁定,获得锁之后再进行操作。

在MySQL中,可以使用排他锁来实现悲观锁,主要就是用到select ... for update语法。

要使用悲观锁,需要关闭mysql数据库的自动提交属性:set autocommit=0;

然后在事务中,通过如下语句对数据进行加锁:

select status from t_goods where id=1 for update

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

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

相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让返回用户错误的信息,让用户决定如何去做。

乐观锁的实现并不会使用数据库提供的锁机制。一般的实现乐观锁的方式就是记录数据版本,如以下SQL:

 update t_goods     
 set status=2,version=version+1    
 where id=#{id} and version=#{version}

二者区别

以上,我们了解了乐观锁和悲观锁的思想以及实现了之后,讨论一下他们的区别。

首先,在加锁时间上有所不同,悲观锁是在事务刚开始的时候就加锁,在拿到锁之后再去进行业务操作。而乐观锁是在更新的那一刻才会进行并发控制,所以是先进行的业务操作。

其次,悲观锁主要是借助数据库的排他锁实现的,而排他锁本质上是一种阻塞锁。 如果并发量比较大并且冲突比较多的时候,会导致很多线程被锁阻塞,导致请求的RT被拉长,并且会占用大量的数据库链接。

相比之下,乐观锁不会造成阻塞,但是他带来的问题就是如果并发的冲突比较高的话,那么就会有很多失败的情况,需要业务代码做好这种失败的特殊处理。

第三点,那就是乐观锁虽然叫锁,但是他并没有额外加锁,它是通过CAS来实现的,所以他的效率比较高,而悲观锁需要利用数据库的锁机制进行加锁,这会带来一定的额外消耗。

还有最后一点,也是比较重要的一点,那就是悲观锁因为做了加锁的动作,所以是会导致死锁的。


高并发不要使用悲观锁

我强烈建议大家,优先使用乐观锁,尤其是并发比较高,并且冲突也比较多的场景。

因为我们前面提到过,悲观锁会有额外的消耗、并且可能会带来死锁。但是这些都不是最重要的。

最重要的是,悲观锁本质上是一种阻塞锁,在并发比较高的情况下,会有很多个线程都被阻塞,而这些阻塞的线程是会占用数据库链接的。所以这时候就会导致你的系统的并发度很低,还有就是这些阻塞的线程的响应时长也会被拉的很长,极度影响用户体验,也会多出来很多慢SQL。

额外提一句,在MySQL 8.0中,已经支持了select ... for update nowait,可以把阻塞锁变成非阻塞的。可以在某种程度上解决悲观锁的阻塞带来的一些问题,但是加锁的额外开销和死锁的问题也还是有的。

所以,高并发场景中,建议大家使用乐观锁,尤其是MySQL 5.x 的版本中,因为不支持nowait,一旦使用悲观锁,会大大降低你的系统的并发度。


 


我的新书《深入理解Java核心技术》已经上市了,上市后一直蝉联京东畅销榜中,目前正在6折优惠中,想要入手的朋友千万不要错过哦~长按二维码即可购买~


长按扫码享受6折优惠




往期推荐

知乎热议:月薪 2~3W 的码农,怎样度过一天?


还在用 SimpleDateFormat 做时间格式化?小心项目崩掉!


入职一家新公司,如何快速熟悉代码?



如果你喜欢本文,
请长按二维码,关注 Hollis.
转发至朋友圈,是对我最大的支持。

点个 在看 
喜欢是一种感觉
在看是一种支持
↘↘↘
浏览 22
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报