1、利用排他锁解决超卖问题
MySQL InnoDB存储引擎支持事务和行锁。如果不手动开启事务的话,InnoDB会为每条SQL语句创建事务,并且update的时候会自动添加排他锁。利用这一特性,我们可以解决超卖问题,具体SQL如下:
update product set stock = stock - 1 where product_id = 1 and stock > 0;
假设某个产品库存数为1,现有两个线程分别为A和B并发执行购买请求,如果A线程先得到排他锁先执行,那么B线程会处于等待状态等待A线程执行完成后再执行,A线程执行时库存数为1,上述SQL语句执行成功,返回结果为1,B线程执行时库存数为0,上述SQL语句执行失败,返回结果为0,这样就有效的解决了产品超卖的情况。
这种方式虽然能解决超卖的问题,但是会出现争锁情况,会出现先请求的用户抢不到锁不能购买产品而后请求的用户抢到锁能购买产品的情况。而且受限于MySQL软件本身,在高并发的场景下支持不是很理想。
2、利用Redis队列解决争锁问题
我们可以利用Redis队列的lpush和lpop方法,解决上述提到的争锁问题。而且redis是非关系型数据库,数据存储在内存里,读写效率快,在高并发的场景下支持要比MySQL好。
这种方式虽然解决了争锁问题,但是又会引入另外个问题——内存爆满,高并发的场景下内存永远是不够用的。
3、利用缓存设置阀值解决内存爆满问题
缓存里面设置阀值可以起到请求过滤的功能,有效的解决内存爆满问题。比如,设置阀值为100,每次请求先判断阀值是否大于0,大于0说明请求有效,存入Redis队列,阀值减1,小于等于0说明请求无效,直接返回结果(即产品售罄)。一般来说,这个阀值的设置要比库存数稍微大点,这样就可以解决用户抢到产品而不付钱的情况。
4、利用分布式锁解决用户多请求问题
为了防刷、防止同一个用户同一秒里面把购物车里的商品进行多次结算,防止前端代码出问题触发两次,我们可以使用分布式锁来解决。
注:上述解决方案只针对秒杀1个产品的情况下。