如何在有限的商品数量的限制下如何保证抢购到商品的用户数不能大于商品数量,也就是不能出现超卖的问题;还有就是抢购时会出现大量用户的访问,如何提高用户体验效果也是一个问题,也就是要解决秒杀系统的性能问题
(1)、在查询商品库存时加排他锁,执行如下语句
select * from goods for where goods_id=#{id} for update
在事务中线程A通过select * from goods for where goods_id=#{id} for update语句给goods_id为#{id}的数据行上了锁。那么其他线程此时可以使用select语句读取数据,但是如果也使用select for update语句加锁,或者使用update,add,delete都会阻塞,直到线程A将事务提交(或者回滚),其他线程中的某个线程排在线程A后的线程才能获取到锁
(2)、更新数据库减库存的时候,进行库存限制条件
update goods set stock = stock - 1 where goods_id = #{id} and stock >0
这种通过数据库加锁来解决的方案,性能不是很好,在高并发的情况下,还可能存在因为获取不到数据库连接或者因为超时等待而报错。
同一个锁key,同一时间只能有一个客户端拿到锁,其他客户端会陷入无限的等待来尝试获取那个锁,只有获取到锁的客户端才能执行下面的业务逻辑
这种方案的缺点是同一个商品在多用户同时下单的情况下,会基于分布式锁串行化处理,导致没法同时处理同一个商品的大量下单的请求
把数据分成很多个段,每个段是一个单独的锁,所以多个线程过来并发修改数据的时候,可以并发的修改不同段的数据
假设场景:假如你现在商品有100个库存,在redis存放5个库存key,形如
key1=goods-01,value=20;
key2=goods-02,value=20;
key3=goods-03,value=20
用户下单时对用户id进行%5计算,看落在哪个redis的key上,就去取哪个,这样每次就能够处理5个进程请求
这种方案可以解决同一个商品在多用户同时下单的情况,但有个坑需要解决:当某段锁的库存不足,一定要实现自动释放锁然后换下一个分段库存再次尝试加锁处理,此种方案复杂比较高
实现思路
update goods set stock = stock - 1 where goods_id = #{id} and stock >0
),返回秒杀订单详情
这种方案的缺点:由于是通过异步队列写入数据库中,可能存在数据不一致,其次引用多个组件复杂度比较高
秒杀系统优化以及解决超卖问题
https://bit.ly/3e7kDVu
电商超卖现象的解决思路
https://bit.ly/2XnYUlz