商品库存扣减方案设计

引言:在库存的变动中,最关键的节点是库存的扣减,在什么时候扣减库存非常重要。目前通用的库存扣减方案有以下几种

  1. 支付后扣减库存,缺点:成功下单的用户,到支付时没有库存可用,导致交易失败。

  2. 下单时就减库存,订单取消再把库存加回来,缺点:恶意刷单不支付导致大量库存被占用,影响商品售卖。

  3. 下单时先预减库存(对应数据库 占用库存加库存操作),支付完成时 释放占用库存(减操作),扣减可用库存。 同时商品在下单时判断商品的实际可售库存 = 可用库存 - 占用库存,如果 > 0,表示可以下订单,这样就不会导致 下单成功,但是支付时没有库存导致失败的场景。

具体方案

  • 订单创建时,通过调用商品接口预占库存,如果预占库存成功,则创建订单,如果预占库存失败,则提示商品库存不足。这个接口必须是同步实时的,因为订单要根据预占库存的结果来判断订单能否创建。
  • 订单支付、取消时,发送mq消息到商品,商品异步消费消息,根据消息的类别去操作库存。如果是未支付取消订单,则释放预占用库存。如果是支付后退款,则需要将可用库存加回来。如果是成功支付,则释放预占库存,并同时扣减可用库存。

需要注意的问题

  • 商品超卖问题
    正常情况下,一个订单过来,其中A商品买了n个,那么我们操作数据库的时候,直接 set stock = stock -n 。这种当然是有问题的,有可能会超卖导致商品库存为负数。当然我们可以在更新db之前,判断库存数是否 > n,如果大于n,再去扣减库存。这总当然可以,不过高并发下,可能依然会导致超卖。当然你可以加锁去保证单线程,不过这样就导致了接口的性能下降。其实sql 可以换个写法 set stock = stock -n where stock >= n。 这样利用了数据库的天然写法保证了商品不超卖。
  • 高并发下的接口性能问题
    步骤一订单创建实时调用商品占用库存接口,因为是实时调用,如果是高并发情况下,对于db占用库存的更新操作可能就会成为性能瓶颈(订单支付或者取消时,因为走了消息队列异步更新数据库,就不存在性能问题)。
    如果解决这个问题呢?业界常用的做法是,将商品的可用库存放到redis中,当订单创建调用占用库存接口时,我们可以利用redis去抗并发,并且redis的命令支持原子性。

1.创建订单扣减缓存中的可用库存

缓存中更新库存和我们去更新数据库时遇到的场景一样,因为要判断库存是否大于下单购买数的逻辑要保持原子性,同时一个订单中需要判断多个商品的库存也是需要原子性,可以结合lua脚本来实现。

  • 首先根据订单明细id查询扣减流水,是否已经操作过,做幂等性校验

  • 然后查询sku的剩余库存,并根据下单购买数做校验,只要有一个sku 数量不足,则返回失败

  • 修改缓存中的剩余库存数

  • 缓存中插入扣减流水记录

2.支付成功后消息队列异步更新数据库中的可用库存

3.订单未支付取消时则需要将缓存中的库存加回来。根据订单明细id查询扣减流水,有扣减流水,则继续查询出订单中所有sku商品的库存,将扣减的库存再加回来。如果没有扣减流水,则跳过不处理。

4、订单已支付退款时,需要同时更新缓存中的库存和db中的库存。

缺点:上面我们说的用lua脚本执行命令,如果一个订单中的多个商品,,一部分成功,一部分扣减库存失败,那么是无法进行回滚操作的,虽然这种可能性很小,所以这种方案我们只能尽量保证redis集群的高可用。以上方案解决了高并发下的接口性能瓶颈,但是因为其复杂性有可能会导致redis中可用库存和数据库中的可用库存不一致(这里说的不一致是指没有未支付订单占用库存的情况),我们可能还需要定时任务去定时维护redis中可用库存和数据库中可用库存的一致性,用数据库中的库存 - 未支付订单的占用库存,然后更新到redis中

总结:以上基于缓存扣减库存,大部分情况是对一些活动的秒杀商品可能才会有如此高的并发,正常情况下也不可能将所用商户的所有商品库存都缓存到redis中,这样也不现实。所以绝大部分流量不高的情况下,我们可以采用数据库占用库存的方式,这种方式简单高效,不易出错。对于一些活动商品,我们则可以单独走缓存扣减可用库存的方式。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,924评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,781评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,813评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,264评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,273评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,383评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,800评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,482评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,673评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,497评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,545评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,240评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,802评论 3 304
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,866评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,101评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,673评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,245评论 2 341

推荐阅读更多精彩内容