为什么库存扣减不需要加锁?尤其是乐观锁?

开发 前端
没加悲观锁是因为秒杀场景不适合用悲观锁(为什么秒杀不能用悲观锁或者分布式锁来实现,详见我的​面试宝典),那么为啥乐观锁都没加呢?

在我的数藏项目中,库存扣减的方法中(本文特指数据库的扣减,不包含 Redis 层),我们全程是没有加锁的,甚至乐观锁都没加。

没加悲观锁是因为秒杀场景不适合用悲观锁(为什么秒杀不能用悲观锁或者分布式锁来实现,详见我的面试宝典),那么为啥乐观锁都没加呢?这个是我们的库存预扣减的 SQL:

图片图片

大家可以先忽略 SQL 中`/*+ COMMIT_ON_SUCCESS ROLLBACK_ON_FAIL TARGET_AFFECT_ROW 1 */`部分内容,这是利用了阿里云 DMS 的 Inventory Hint 的机制抗高并发的实现。具体可以到我的项目课程中学习。

如上的 SQL 中,我们只做了数量的扣减,以及数量的上限的控制。但是没有通过 lock_version 做乐观锁的版本控制。

虽然我们用了MybatisPlus。也用到了他的@Version 注解,但是我们自己写的 SQL 的这种方式,MyBatisPlus 也不会主动帮我们加乐观锁的控制:

图片图片

那就是说我们这里没有做加锁,为什么呢?

如果你知道乐观锁和悲观锁的区别,那么就会知道,乐观锁因为比较乐观,所以一般是先做业务逻辑操作,比如参数处理,内存中进行模型组装调整,然后再去更新数据库。悲观锁因为比较悲观,所以会先尝试加锁,然后再去做业务逻辑操作。

也就是说,乐观锁是先干活,后加锁。悲观锁是先加锁,再干活。乐观锁适合用在并发冲突不高的场景中。

而我们的这个库存扣减这里,其实并发冲突还挺高的,如果这里用了乐观锁,那么就会有大量的失败。

举个例子,100个线程同时来查询库存,得到的 lock_version = 1,然后同时去更新库存,都要求当前的 lock_version = 1,这就会导致99个线程都失败,那么就会导致整体失败,如果事务中有其他操作,就要回滚。

库存扣减的时候,我们的目的肯定是让多个线程都扣减成功,并且不扣错就行了。

如果你的 SQL 是这样的:

UPDATE collection


SET saleable_inventory = #{saleableInventory}


WHERE id = #{id}

也就是说这个变更后的saleableInventory是你自己算好的,给到 SQL 去执行的,那么就必须要加锁来避免并发的时候计算出错,但是我们通过在 SQL 中扣减不会有任何并发导致出错的问题。

而如果这里不加乐观锁,只通过库存扣减,以及库存不能小于0来做控制,那么只需要他们各自抢锁然后按顺序扣减就行了,反正每次库存都是在前面的结果上依次扣减的:

SET saleable_inventory = saleable_inventory - #{quantity}

所以,这里我们只需要保证不会超卖就行了,剩下的并发问题 update 过程中的锁会帮我们解决的。

本文的技术方案来自于我出的数藏项目实战课,里面还有很多类似的干货内容。

责任编辑:武晓燕 来源: Hollis
相关推荐

2014-01-17 13:09:48

Linux碎片整理

2021-07-06 11:10:22

LinuxmacOSWindows

2021-05-07 15:18:26

比特币禁令监管

2019-07-15 08:00:00

AI人工智能

2021-04-27 08:52:55

Linux MacOS碎片

2012-06-12 09:46:21

Linux碎片整理

2020-05-07 10:09:30

MAC地址网络协议网络

2014-09-24 09:31:31

Dockersshd

2021-07-02 09:00:00

自动驾驶特斯拉技术

2022-06-02 16:04:02

DeFi金融比特币

2017-03-13 13:54:40

戴尔

2015-07-29 09:42:09

工程师全栈工程师

2022-03-22 13:39:10

框架react面试

2012-08-23 09:50:07

测试测试人员软件测试

2009-11-23 12:45:22

2019-02-25 08:57:22

5G6G网络

2015-09-30 09:57:53

天分热情工程师

2013-12-02 09:43:29

字符串编程

2015-08-20 10:56:19

算法界面开发

2024-02-22 09:00:00

LogitMat数据集算法
点赞
收藏

51CTO技术栈公众号