库存负数

库存负数

  • 场景:

当==多用户==并发点击参与活动,如:抽奖活动,这个时候奖品只有一个库存了,理论上只有一个用户可以获得,但是并发的时候往往会出现他们都成功获得奖品,导致奖品多支出,加大了活动成本

  • 剖析:

有问题的逻辑流程一般是这样的:

中奖 --> 查询奖品库存 --> 有 --> 更新奖品库存 --> 添加中奖纪录 --> 告知中奖
中奖 --> 查询奖品库存 --> 无 --> 告知无中奖

假设抽奖活动,当前奖品A只有最后一个库存,然后用户A、B、C,同时参与活动同时中奖奖品都是A,这个时候查询商品库存是存在1个,就会进行更新库存,添加中奖纪录,然后就同时中奖了

  • 解决方案:

最理想根本就不需要用多做一个库存的SELECT奖品库存操作,只需要UPDATE 奖品库存-1 WHERE 奖品库存>=1,UPDATE成功后就说明是有库存的,然后再做后续操作,并发的时候只会有一个用户UPDATE成功


  • 方案1
    • [主动]后台点击更新缓存按钮,从DB查找最新数据集合,删除原缓存数据,存储新数据到缓存;
    • 问题:更新过程中删除掉缓存后刚好有业务在查询,那么这个时候返回的数据会是空,会影响用户体验

  • 方案2
    • [被动]前台获取数据时发现没有缓存数据就会去数据库同步数据到缓存
    • 问题:当并发请求获取缓存数据不存在的时候,就会产生并发的查询数据的操作。

  • 方案3
    • [主动]后台点击更新缓存按钮,从DB查找最新数据集合,这里不删除缓存,通过遍历数据覆盖和删除掉无效的数据
    • 问题:逻辑相对麻烦,而且更新机制无法通用;

推荐


以上的几种更新方案我都遇到过,因为产生了各种问题,所以我想到了一个相对好的方案,类似预加载功能,先把数据加载到缓存中,缓存成功后再切换显示最新的数据,将旧数据设置过期;

  • 方案4
    [主动][预加载]前台获取缓存的时候需要先得到缓存数据对应的Redis Key(简称:[ShowingKey]),然后根据[ShowingKey]去读取缓存数据(简称:[缓存];
    • 需要两块数据:
      • [ShowingKey](可以是最近一次更新缓存的时间戳或者根据自己规则自定义)
      • [缓存](需要缓存的数据,如:DB数据等)

    • 举个栗子:
      我们现在有个业务需要缓存今日上新商品数据,缓存到Hash中

      • [缓存]对应Redis Key 规则
        • Hash Key=Goods:Todays:{0} {0}=时间戳
      • [ShowingKey]对应的Redis Key
        • Key string key=Goods:Todays:ing 内容=最近一次的更新时间戳

      更新逻辑:

      后台编辑人员操作完数据的时候点击更新按钮,获取服务器当前时间=1469938351000=[更新时间戳],然后获取DB数据,缓存到Goods:Todays:1469938351000中,添加缓存数据成功后,获取Goods:Todays:ing中的时间戳1449538371020=[上一次更新时间戳],更新Goods:Todays:ing值=[更新时间戳]=1469938351000,更新成功后可以把[上一次更新时间戳]对应的缓存设置过期时间,我一般是设置5秒后过期。(注意旧数据一定要设置过期时间,不能直接删除,因为在切换[ShowingKey]的过程中可能还有业务在使用)

      数值累加,get,set+1并发导致累加不准确

      • 使用redis increment 自增数值的机制不会有累加不准确的问
      • ++++++++++++++
         

        综合套路

        • 图片地址独立域名
          • 与业务不同域名可以减少请求头里不必要的cookie传输
原文地址:https://www.cnblogs.com/agang-php/p/9810677.html