【253期】京东二面:商品库存的扣除过程中,如何防止超卖问题?

点击上方“Java精选”,选择“设为星标”

别问别人为什么,多问自己凭什么!

下方有惊喜,留言必回,有问必答!

每一天进步一点点,是成功的开始...

在商品购买的过程中,库存的抵扣过程,一般操作如下:

1. select根据商品id查询商品的库存。

2. 根据下单的数量,计算库存是否足够,如果存库不足则抛出库存不足的异常,如果库存足够,则减去扣除的库存得到最新的库存剩余值。

3. set设置最新的库存剩余值。

上述过程的伪代码如下:

// 根据商品id获取商品剩余库存
select stock_remaing from stock_table where id=${goodsId};
 
// 操作库存
// 比较库存
if(stock_remaing <quantity){
   // 抛出库存不足的异常
}
else{
// 抵扣以后的库存值
int new_stock=stock_remaing - quantity;
}
 
// 根据商品id设置计算后的库存
update stock_table  set stock_remaing =${new_stock} id=${goodsId};

并发修改数据库存超卖

如果数据库事务的隔离级别不是串行化(serializable),根据事务的特性,在并发修改的时候,可能会出现写覆盖的问题。

推荐下自己做的 Spring boot 的实战项目:
https://gitee.com/yoodb/jing-xuan‍

假设,商品的剩余库存stock_remaing 为100,客户A下单20,客户B下单30,在并发扣库存的时候,可能存在超卖。如果客户A和客户B同时获取剩余库存为100,则会出现事务后提交的值会覆盖前一个客户提交的值,有可能剩余的库存是80或者70。流程如下:

aa40fafd03a908389007b3108f13801c.png

加锁更新存库

为了在事务控制中,防止写覆盖,你会想到使用select for update的方式,将该商品的库存锁住,然后执行余下的操作。流程如下:

483c92c363cdfa5f176e80e8a16ef72e.png

以上,使用悲观锁方式,在分布式服务中,如果并发情况比较高的时候,扣减库存的操作是串行操作,效率很低。

使用乐观锁的方式更新

在更新的时候,使用(CAS+版本号更新)+重试条件(重试次数或者重试时间限制)乐观锁的方式更新库存(乐观锁的操作过程可以参考使用Spring AOP+注解基于CAS方式实现java的乐观锁)。

推荐下几个月熬夜整理的近 10000+ 面试资料大全:https://gitee.com/yoodb/eboo‍ks

此时,如果,客户A和客户B同时读取到库存剩余100,在更新的时候,有一个操作会失败。流程如下:

17c7517d04aa1050437bd18e0aee9009.png

该种方式可以大大提高并发性,也可以保证数据的一致性;通过重试次数和重试时间的条件控制,可以防止过多的重试带来的数据库压力。

可以使用直接递减的方式执行么?

在抵扣库存的时候,有的人提议不执行select,计算,set三段式的操作,直接扣减的方式,并且对于扣减到小于零的情况作了判断。伪代码如下:

update stock_table set remaing_stock=remaing_stock-${quantity} 
where id =商品id
and remaing_stock>${quantity};

在分布式服务调用中,因为网络异常,获取服务器异常,可能在微服务调用时,存在服务重试。例如,场景的网关超时,服务重试机制。此时,该种方式不满足幂等性,而存在多扣的情况。例如,同一用户扣减库存时,服务重试,极端情况下,该用户扣减库存操作执行多次,则就出现了商品超卖。

可以使用redis进行库存的抵扣么?

由于没有研究过redis源码,对于这种方式参考了大牛的回复,答案是可以使用redis的事务性扣减余额,但在CAS机制上比mysql没有优势,高性能是因为其内存存储的原因,带来的副作用是数据有丢失风险。

版权声明:本文为CSDN博主「iloveoverfly」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

https://blog.csdn.net/new_com/article/details/105568124

公众号“Java精选”所发表内容注明来源的,版权归原出处所有(无法查证版权的或者未注明出处的均来自网络,系转载,转载的目的在于传递更多信息,版权属于原作者。如有侵权,请联系,笔者会第一时间删除处理!

------ THE END ------

4850d67021fae702bcce853a60f02ebf.png精品资料,超赞福利!33f051387d115abd76e3104573925a80.png

>Java精选面试题<
3000+ 道面试题在线刷,最新、最全 Java 面试题!

ac7e432000e1049512aa6f0a9c2b02fd.png

b5454742d6dc2b7a880ee887ad2a9137.png

期往精选  点击标题可跳转

【245期】面试官:同类中两个方法加同步锁,多个线程支持同时访问这两个方法吗?

【246期】Git 常用命令,每条都很重要!

【247期】自从用了 OkHttp3,别的都完全不想用了!

【248期】如何在 Spring Boot 中使用异步方法优化 Service 逻辑提高接口响应速度?

【249期】Mybatis 源码解读 — 9 种设计模式,真是太有用了!

【250期】面试官问:谈一谈 MQ 消息幂等(去重)通用解决方案?

【251期】分享一款基于 SpringBoot 和 ElementUi 的 HC 小区物联网平台,附源码!

【252期】爆赞,对 volatile 关键字讲解最好的一篇文章!

89f065d6ab8d2d4ed0993343271b755e.png 技术交流群!16d0021b52d8e8595c55015192fc4624.png

最近有很多人问,有没有读者交流群!想知道如何加入?方式很简单,兴趣相投的朋友,只需要点击下方卡片,回复“加群”,即可无套路入交流群!

文章有帮助的话,在看,转发吧!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值