美文网首页技术干货
电商技术 -- 库存设计指北

电商技术 -- 库存设计指北

作者: 殷天文 | 来源:发表于2020-03-15 12:55 被阅读0次

前言

最近在解决一套老电商系统的库存"超卖"问题。一直以为超卖问题,最难解决的是库存扣减,实则不然,我们的系统在解决了库存扣减问题之后,还会一直有“超卖”现象?这一切的背后到底是道德的沦丧,还是人性的扭曲,欢迎收看本期走近科学

本文带你解决以下电商场景问题

  1. 保证库存线程安全的扣减
  2. 防止库存的多次扣减、回滚
  3. 超时未支付被取消的订单(取消会回滚库存), 如果收到了支付回调怎么办

如何线程安全的扣减库存

先来说说库存扣减的问题,这是我们原来老系统的逻辑,注意!这里是错误的示例

// 以下是伪代码,错误的示例
// 查询出Goods对象
$goods = selectGoodsById($id);
if ($goods->num - $order_num > 0) {
    // 计算出扣减后的库存
    $goods->num = $goods->num - $order_num;
    // 保存
    save($goods);
}

上述代码犯了大忌,并发情况会导致多个线程读到相同的库存数,然后扣减,然后保存到DB,下面我们来说下正确的姿势

正确的做法

利用MySQL update 会持有当前记录锁的特点,保证线程安全的扣减

SQL 示例:

update kucun set num = num - ? where id = ? and num - ? >= 0

我们的这条记录根据主键更新,当事务A update 这条记录时,会持有当前记录的锁,当事务A未提交时,其他想要更新这条记录的事务只能等待锁释放

关于MySQL update 锁的细节,本文不讨论,可以参考MySQL文档

https://dev.mysql.com/doc/refman/8.0/en/innodb-locks-set.html
https://dev.mysql.com/doc/refman/8.0/en/innodb-transaction-isolation-levels.html
https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html

虽然MySQL可以保证数据的准确性,但是大并发量场景下,大量的锁竞争,导致库存的扣减可能成为系统性能的瓶颈

使用 Redis 库存扣减

使用Redis的优势很多,单线程的文件事件处理器保证了并发下可以线程的安全扣减、回滚库存, 以及Redis高性能。

虽然Redis解决了线程安全和性能的问题,但是Redis并不能做到像MySQL那样一条SQL命令完成库存扣减,我们需要先读出已有库存,再和当前下单库存做一个判断是否可以库存扣减。所以最佳的实现方案是通过Redis 执行lua脚本,保证整个逻辑处理期间,不会有其他客户端插进来

    /**
     *
     * 扣减库存Lua脚本
     * 库存(stock)-1:表示不限库存
     * 库存(stock)0:表示没有库存
     * 库存(stock)大于0:表示剩余库存
     *
     * @params 库存key
     * @return
     *      -3:库存未初始化
     *      -2:库存不足
     *      -1:不限库存
     *      大于等于0:剩余库存(扣减之后剩余的库存)
     */
    const SUB_STOCK_LUA = "
        if (redis.call('exists', KEYS[1]) == 1) then
            local stock = tonumber(redis.call('get', KEYS[1]));
            local num = tonumber(ARGV[1]);   
            if (stock == -1) then
                return -1;
            end;
            
            if (stock >= num) then
                return redis.call('incrby', KEYS[1], 0 - num);
            end;
            
            return -2;
        end;
        
        return -3;
    ";

注意:
当对一个订单中的 good_list 扣减库存的时候,需要注意,当某一个商品库存扣减失败时,之前的扣减的商品库存需要回滚。这会涉及到对redis的多次操作,你可以把整体逻辑写到一个lua脚本中

使用Redis 做库存扣减会有一个问题(伪代码如下),Redis数据和MySQL数据并不能保证强一致性,因为Redis的数据相当于直接写进去了,如果在需要回滚的时候,Redis不可用了导致数据无法回滚,最终会造成MySQL没有写入订单数据,Redis却扣减了库存

try {
    $db->beginTransaction();
    
    $db->saveOrder();
    $redis->reduceStock();

    $db->commit();
        
} catch (Exception $e) {
    $db->rollback();
    $redis->rollbackStock();
}

这种情况并没有什么好的解决办法,这是一个几率非常小的故障,首先我们肯定要尽可能的保证Redis的高可用性,其次在发生这种情况后,我们要想办法恢复Redis中的数据,例如我们可以在整个逻辑之后,选择异步的方式(例如MQ)向MySQL中同步库存,当发生故障后,以MySQL数据为准恢复数据

所以Redis是一把双刃剑,提升性能的同时,也带来了问题

AliSQL

这是后来我在网上看到的方案,AliSQL 是阿里自研 MySQL 分支,AliSQL 针对并发修改同一记录的情况,使用数据库层面的缓冲队列,避免大量争锁的代价。感兴趣的同学可以试下(阿里云MySQL 8.0 集成了这一功能),如果AliSQL解决了性能问题的话,那么这个方案相比Redis要更好

关于库存多次扣减的问题

当订单的提交和库存的扣减同步进行的时候,不需要考虑这个问题。

举例:订单系统生成订单之后,通过MQ通知库存系统,库存系统异步扣减库存,这个时候库存系统可能会多次消费,这个时候就需要考虑这个问题了。

或者我们上面说的通过MQ同步MySQL库存也需要考虑可能发生多次扣减

解决方案如图,通过订单做为唯一索引保证流水记录的唯一性,从而保证只能有一次成功的扣减

image.png

库存回滚问题

多数博客对于超卖的讲解只在于库存的扣减,但是库存扣减安全了,真的就可以保证不超卖吗?我们的系统在解决了库存扣减问题后,还是出现成交订单 > 库存的问题,为此我也是绞尽脑汁,抓破了头

在对下单进行压力测试之后,我坚信下单不会出现超卖的问题,后来我怀疑问题出在了库存回滚,如果一个订单回滚了两次库存(取消超时未支付订单的线程和用户线程同时取消一个订单),同样也会出现超卖的现象。

解决方法:
和防止多次扣减一样,采用写入订单回滚流水的方式,个人认为这种方法比较加锁要好,数据有迹可循

超时未支付被取消的订单收到了支付回调

在解决了库存回滚问题之后,超卖问题还没有解决,最后通过日志定位到了这个问题。

问题描述:用户在系统即将自动取消订单的前一瞬间完成了支付,系统取消了该订单并回滚了库存,同时系统收到了该订单的支付回调,该订单的状态更改为已支付,因为不该出现的库存回滚导致了“超卖”

下面说下我们的解决方案,以微信支付为例

我们的系统在提交订单之后,会调用微信的统一下单接口,这时候微信收到了我们的商户订单号(微信已经生成订单),用户选择不支付。超时自动取消逻辑处理之前,先调用微信的关闭订单接口,如果关闭成功,则这个时候用户后续无法对该订单发起支付。如果返回订单已支付,则无需处理该订单,该订单会收到微信支付的回调

参考

https://www.jianshu.com/p/76bc0e963172
https://www.zhihu.com/question/268937734
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_3

相关文章

  • 电商技术 -- 库存设计指北

    前言 最近在解决一套老电商系统的库存"超卖"问题。一直以为超卖问题,最难解决的是库存扣减,实则不然,我们的系统在解...

  • 总结-转载

    1.怎样从0一步一步搭建用户激励体系 2.小程序 3.电商解密:电商下单选仓的逻辑 电商技术解密之库存系统 电...

  • 电商库存系统怎么选择?

    电商库存管理一直是困扰着电商运营者的重要问题之一,也是电商网店不能回避的问题之一。如果电商库存管理没有做好,电商网...

  • 电商

    12.6刘润商学院日课感悟:电商 实体店电商面临的最大问题还是库存,库存是影响着资金的流动。 同样电商可以...

  • 索引表和ES的一点点思考

    索引表设计 在电商项目中,物理库存系统是个极其重要的系统,订单支付后,就会开始来占用物理库存。一般情况下,库存系统...

  • 电商库存

    一、销售层 销售层的库存决定是否可售卖,下单是否能成功。在秒杀时,活动 库存决定了是否可以秒杀成功;在预售时,预售...

  • 商城系统库存问题分析

    电商的库存设计,是后台的重点,也是难点,关乎商品是否存在超卖。商品的库存增加方式倒不难,直接在后台添加即可,而扣减...

  • 合肥热门电商设计培训机构【免费试听】2018-08-22

    合肥热门电商设计培训机构【免费试听】 一、合肥热门电商设计培训机构,合肥天琥教育电商设计​‌‌培训班,合肥电商设计...

  • 觅宝首页改版解决方案-- - 草稿

    以溯源技术支持的电商平台首页设计,主要特点是产品溯源,主要功能是电商服务,主要合作对象是品控市场。 用溯源技术为电...

  • 谈谈跨境电商公司订单管理系统中订单仓库和发货仓库相关方案设计

    跨境出口电商相比国内电商、跨境进口电商来讲,最大的区别点可能在于物流渠道众多以及售卖虚拟库存。目前跨境电商发给买家...

网友评论

    本文标题:电商技术 -- 库存设计指北

    本文链接:https://www.haomeiwen.com/subject/gplvdhtx.html