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

科技   2024-09-24 17:11   江苏  
作者 l Hollis
来源 l Hollis(ID:hollischuang)

库存扣减的方法中(本文特指数据库的扣减,不包含 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 过程中的锁会帮我们解决的。


往期推荐

4 种 MySQL 同步 ES 方案,yyds!

公司要双休了?不少员工强烈反对。。

写出屎山代码的 12 个技巧 ,一定得会 !

阿里云盘照片泄漏到底是什么原因?

这10种分布式ID,真香!

小漏洞大破坏,那些“名垂青史”的病毒

为什么裁员先裁技术?房子盖好了不需要农民工了!!!

这里有最新前沿技术资讯、技术干货等内容

点这里 ↓↓↓ 记得 关注✔ 标星⭐ 哦



码小辫
用堆栈的方式来学习技术,用遍历的方法来获取资料,用AI的思路考虑人生……码农的AI世界、BUG的程序员人生……hello world!
 最新文章