在我的数藏项目中,库存扣减的方法中(本文特指数据库的扣减,不包含 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 过程中的锁会帮我们解决的。
本文的技术方案来自于我出的数藏项目实战课,里面还有很多类似的干货内容。
这个项目用到了目前市面上最主流的 SpringCloudAlibaba 的技术栈,用到的框架都是目前的最新版+稳定版。(项目详细介绍)
后端主要用到了像 JDK 21、Spring 6.1、SpringCloud 2023、Nacos、Sentinel、Dubbo、Redis、XXL-JOB、RocketMQ、ShardingJDBC、Druid、MySQL、EslasticSearch、Canal、 Seata、SaToken 等中间件及技术,还用到了像 Hutool、Logback、Caffeine、Mybatis、MybatisPlus、FastJson2等常用的开源框架。
在技术方案上,主要涉及到了各种分布式、微服务、高并发、高可用等相关技术列表。(项目详细介绍)
项目给大家交付的内容包括了代码+视频+文档+答疑。
这个项目目前还在更新中,预计会在接下来的3个月左右时间完成代码的开发、文档和视频的更新。(主干功能已完成,项目可完整运行起来)
更新完之后,有效代码行数应该在3万行左右,视频和文档都在200集左右。文档总字数大概20万字左右,视频的总时长大概在2000分钟左右。
这个项目因为目前刚刚推出,还是一个首发价,当前的价格是179,这个价格不是一年的价格,是永久的。
项目详细介绍:高并发、大流量的项目实战课上线了!
购买的方式目前大家可以通过下方的二维码下单,下单后会有短信提示,然后你就可以根据提示操作,申请代码、视频、文档的权限了。
这个项目是一个微服务的技术栈,所以其实内容还是挺多的,想要学明白,还是有一定的门槛的,所以,对于0基础的人不适合!
除了0基础以外,其他人都能学,因为项目中我有很多模块,不同的模块遇到的挑战、用到的技术都不一样,你可以按照我划分的难度进行选择性学习。