商品扣减库存好一点儿的方式(只通过数据库处理)

前言

相信很多小伙伴在面试一些有涉及订单相关的公司时,技术面都有:“商品扣减库存怎么操作?你的SQL是怎么写的?”

如果你回答说,就很简单啊:

update stock_table set stock=#{扣减后的库存} where product_id=#{product}

那我估计面试官就会对你摇摇头了

正文

我们分析在商品购买的过程中,库存的扣减过程

  1. 根据商品id查询商品的剩余库存
  2. 根据下单的数量,计算剩余库存是否足够,如果库存不足就返回相应的提示;相反,则减去扣除的库存得到最新的库存剩余值
  3. 更新最新的库存剩余值

写点儿伪代码


// 根据商品id获取商品剩余库存
select stock from stock_table where id=#{productId}

// 比较库存
if(stock_remaing <quantity){
// 返回库存不足的提示
}
else{
// 抵扣以后的库存值
int new_stock=stock - quantity;
}

// 根据商品id设置计算后的库存
update stock_table  set stock =#{new_stock} id=${productId}

如果是并发状态下呢??

根据事务的特性,在并发修改的时候,可能会出现写覆盖的问题。

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

流程如下:
在这里插入图片描述
所以上面这种方式就依旧出现问题了,最后引起的问题一定会是超卖…

超卖问题如果从只用数据库解决,有两种解决方案

1.悲观锁方案:

每次获取商品时,对该商品加排他锁。使用select for update的方式,也就是在用户A获取获取 id=1 的商品信息时对该行记录加锁,期间其他用户阻塞等待访问该记录。

SELECT…FOR UPDATE 语句的语法如下:
  SELECT … FOR UPDATE [OF column_list][WAIT n|NOWAIT][SKIP LOCKED];
其中:
  OF 子句用于指定即将更新的列,即锁定行上的特定列。
  WAIT 子句指定等待其他用户释放锁的秒数,防止无限期的等待。

begin;

select * from goods where id = 1 for update;

update goods set stock = stock - 1 where id = 1;

commit;

注:上面的begin/commit为事务的开始和结束,因为在前一步我们关闭了mysql的autocommit,所以需要手动控制事务的提交,在这里就不细表了。

for update的注意点
for update 仅适用于InnoDB,并且必须开启事务,在begin与commit之间才生效。

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

2. 乐观锁方案

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

在这里插入图片描述

该种方式可以大大提高并发性,每次去更新库存数值的时候,都需要校验本次的库存数和对应的版本号是否为刚拿到的时候一样(证明更新前无其余线程抢先做更新),如果不一致,证明有线程抢先对库存有更新,则库存数和版本号不一致,就会更新失败,然后我们就可以通过重试次数(CAS思想)和重试时间的条件控制,这样可以保证数据的一致性,额外防止过多的重试带来的数据库压力。

但是与此同时也引出了下一个问题就是,所有的访问,都直接到了数据库这一层,给数据库带了的压力就大了,所以在实际的过程中可以再把 Redis 用上,事务性扣减库存,额外写一些自己业务上的逻辑。
但是Redis 在CAS机制上比mysql没有优势,高性能是因为其内存存储的原因,带来的副作用是数据有丢失风险。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值