商品库存的扣除过程,如何防止超卖?

简介: 1、select根据商品id查询商品的库存。

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


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),根据事务的特性,在并发修改的时候,可能会出现写覆盖的问题。


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


流程如下:


image.png


加锁更新存库


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


流程如下:


image.png


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


使用乐观锁的方式更新


在更新的时候,使用(CAS+版本号更新)+重试条件(重试次数或者重试时间限制)乐观锁的方式更新库存。此时,如果,客户A和客户B同时读取到库存剩余100,在更新的时候,有一个操作会失败。


流程如下:


image.png


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


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


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

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

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


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


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


原文链接:https://blog.csdn.net/new_com/article/details/105568124


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


相关文章
|
缓存 索引
ES经典面试题:谈谈filter和query有什么区别?
ES经典面试题:谈谈filter和query有什么区别?
1030 0
ES经典面试题:谈谈filter和query有什么区别?
|
前端开发 JavaScript Java
一文了解主流开发语言都有哪些!
本文将综合探讨目前市场上最流行、最多人使用的几种主流开发语言,包括它们的特点、典型应用场景以及简单示例代码。
|
存储 数据库
如何在数据库中存储小数:FLOAT、DECIMAL还是BIGINT?
【8月更文挑战第7天】在数据库中存储小数时,需谨慎选择数据类型:FLOAT、DECIMAL 或 BIGINT。FLOAT 存储空间小,适于非关键性小数如温度;但精度有限,可能产生误差。DECIMAL 能精确表示小数,适合货币金额等需要高度准确性的场景,不过占用空间较大。BIGINT 用于整数,若存储小数需额外转换处理。根据精度需求及应用场景选择合适类型至关重要。
1068 2
|
Java UED
Java多线程实现文件上传详解
文件上传是Web应用程序中常见的功能之一,用户可以通过网页将文件从本地计算机上传到服务器。在处理大文件或多用户并发上传的情况下,为了提高性能和用户体验,常常使用多线程来实现文件上传功能。本文将详细介绍如何使用Java多线程实现文件上传,包括上传原理、多线程实现、代码示例等内容。
703 0
|
存储 算法 Java
Java ZGC 深度剖析及其在构建低延迟流系统中的实践心得
ZGC 是 Java 的一种低延迟垃圾回收器,旨在减少 STW 时间,提高对延迟敏感应用的性能。它通过并发和分区收集技术实现这一目标,确保 STW 时长可预测且低至亚毫秒级别。ZGC 使用了着色指针、读屏障、区域化内存管理和压缩迁移等技术。读屏障确保对象在 GC 期间的正确加载,而区域化内存管理则允许高效地分配和释放内存。ZGC 自动调整配置以适应不同应用,且支持大范围的内存大小。通过调整参数如堆大小、GC 线程数、动态 GC 策略等,可以进一步优化延迟。AutoMQ 在使用 ZGC 后,成功将 STW 时间降低到 50 微秒以下,提升了服务性能。
Java ZGC 深度剖析及其在构建低延迟流系统中的实践心得
|
SQL 分布式计算 运维
hadoop日常运维白皮书
hadoop日常运维与升级总结 ▲进程管理由于配置文件的更改,需要重启生效,或者是进程自己因某种致命原因终止,或者发现进程工作出现异常等情况下,需要进行手动进程的关闭或启动,或者是增删节点过程中的需要,进程的关闭与启动,使用hadoop-daemon.
2545 0
|
存储 移动开发 前端开发
【Web 前端】H5新特性有哪些?
【4月更文挑战第22天】【Web 前端】H5新特性有哪些?
|
数据采集 并行计算 安全
Python中的多线程与协程的比较与应用场景
Python中的多线程与协程的比较与应用场景
1254 0
|
NoSQL Java 数据库
史上最全的java分布式锁的5种实现方式
史上最全的java分布式锁的5种实现方式
936 0
史上最全的java分布式锁的5种实现方式
|
存储 人工智能 容灾
华为分布式存储专为海量非结构化数据而生,助力构建坚实数据底座
全球每年产生的数据总量将于2030年达到1YB,其中95%以上数据为非结构化数据,企业亟需打造混合负载更高效、数据管理更简单、数据韧性更强悍的数据底座来应对未来的数据挑战。面对海量非结构化数据,构筑架构与技术均遥遥领先的华为OceanStor分布式存储系列将帮助企业释放数据价值,顺利拥抱YB数据时代。