资源抢购
一、背景
资源抢购问题,如优惠券、红包等抢购问题。在高并发的情况下会引发库存超卖、红包超出个数等问题
二、优惠券抢购
三、超卖
在修改数据库的时候会存在库存扣减超卖,出现负数的情况。如何保证优惠券不超卖:
通过数据库乐观锁。下面是三个乐观锁方案
1)update product set stock=stock-1 where id = 1 and stock>0
id是主键索引的前提下,如果每次只是减少1个库存,则可以采用上面的方式,只做数据安全
校验,可以有效减库存,性能更高,避免大量无用sql,只要有库存就也可以操作成功.
场景:高并发场景下的取号器,优惠券发放扣减库存等
2)update product set stock=stock-1 where stock=#{原先查询的库存} and id = 1 and stock>0
使用业务自身的条件做为乐观锁,但是存在ABA问题,对比方案三的好处是不用增加version版本字段。如果只是扣减库存且不在意ABA问题时,则可以采用上面的方式,但业务性能相对方案一就差了点,因为库存变动后sql就会无效
3)update product set stock=stock-1,versioin = version+1 where id = 1 and stock>0 and version=#{原先查询的版本号}
增加版本号主要是为了解决ABA问题,数据读取后,更新前数据被别人篡改过,version只能做递增
场景:商品秒杀、优惠券方法,需要记录库存操作前后的业务四、单用户多抢
同一个用户抢到了多张相同的优惠券,导致了黑客通过脚本抢用优惠券。
解决方案:分布式锁
在进行抢购优惠券的时候进行一个加锁,通过分布式锁控制优惠券资源的抢占。这个时候分布式锁的控制颗粒度只需要细到用户上,不用对整个优惠券的进行加锁。因为主要是解决相同用户抢购多个优惠券问题。
//如redis锁的key
String lockKey = String.format(CacheKey.PROMOTION_COUPON_LOCK_KEY,couponId,Context.getTokenContext().getUserId());五、抢红包
抢红包功能主要有两个步骤,先是发红包,然后再抢红包。这个时候如何放在红包超发
解决方案1:
发红包:在发红包的时候对红包的金额和个数进行计算,然后插入到redis或者数据库当中。
抢红包:在抢红包的时候通过对红包进行一个判断,通过lua脚本,判断红包是否被抢完,再进行获取。入库操作可以异步进行,使用异步线程或者mq(根据实际需求决定是否同步入库)
