如何设计秒杀系统的库存系统

一、表结构设计

item_idstock_count
110

每个用户下单,进行update,对这一行的库存进行扣减

二、如何防止超卖?

不能先查库存在进行扣减,多个线程会有超卖现象,使用如下SQL语句:

update stock set stock_count=stock_count-1 
	where item_id=1 and stock_count>=1

这条SQL语句持有了这个行锁,底层是串行执行的

三、库存数据放在Redis里的问题

库存是否要redis里面去操作,需要考虑redis本身的高可用问题

扣减库存的同时还要同时写一张库存流水表(分布式事务的解决方案),这两个操作是在一个事务当中的,如果把库存放到redis中,写库存流水表无法去保证原子性。

四、如何解决高并发问题?

并发资源的争抢,提升并发度:降低锁的粒度、降低锁持有的时间

1、降低锁的粒度

可以采用分库分表方案

2、降低锁持有的时间

内存里面设计一个队列,200ms的请求都放到一个队列里面,用户同步等待。让200ms内的多个用户请求都放在队列中,将这个队列请求合并后批量的进行一次数据的IO,批量的进行一次扣减

计数商品的并发请求,如果并发数超过了阈值,才会去创建队列。

五、难点问题解决

1、异步合并请求的时候报错,上游超时了

这个时候有可能上游服务显示没有扣减成功,但是其实已经扣减成功了,导致数据不一致,上游发出消息(携带订单号)对库存进行回滚,这是上面的那个库存流水表就起作用了。类似seata,如果已经插入库存流水表,针对这条记录进行回滚。

2、库存只有1件了,但是内存合并之后,需要去扣3件

异步线程合并去扣减不成功,退化成一个循环,每个用户购买的数量从大到小进行扣减,尽量保证买的多的用户线扣减成功,

3、优化点:降低锁持有的时间

事务(耗时t1 + t2):
update 库存表 耗时t1
insert 库存流水表 耗时t2
or
事务(耗时t1):
insert 库存流水表 耗时t2
update 库存表 耗时t1
选择第二种事务的SQL执行顺序,锁持有时间更短。

4、库存也是有缓存的,如何保证数据库和缓存的数据一致性?

cache aside 先更行数据库,在删缓存
改造为:监听binlog,代码不用关心缓存

5、缓存命中率低,刚更新为一个值,马上有更新成另外的值

监听binlog的时候,设计一个时间窗口,一个商品在5s之内有更新,只在5s结束的时候去更新缓存,避免频繁覆盖

6、binlog是并行过来的,后面的可能先被监听到,消息乱序的问题?

在表里面增加版本号,每次更新对版本号进行+1,监听binlog的时候,判断下redis的版本号,判断大于的还是更新,避免老的binlog把新的缓存值覆盖掉

7、异步刷新之后真实的库存和页面的库存有延迟?库存有没有比数量更重要

一旦扣库存失败,会把缓存里的数据同步更新成0

8、多个线程去判断版本号,会不会出现并发安全问题?

需要加分布式锁

9、内存队列很难横向扩容

数据库层做排队,应用层只能做到单机排队,单用用技术本身很多,这种排队方式控制并发仍然有限,所以如果能在数据库层做全局排队是最理想的,淘宝的数据库团队开发了针对这种MySQL的innoDB层的patch,可以做到数据库层上对单行记录做到并发排队。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值