• Redis秒杀相关点


    一、秒杀的特点

    1. 瞬时并发量很大

      qps可能达到几十万,上百万。一般数据库只能支持千级别的并发量,而redis单节点数万的并发量支持,所以可以使用redis来处理大部分请求,最终少量请求进入DB,

    2. 读多写少

      秒杀是大量用户针对少量的库存进行抢,所以最终的下单并不会特别多,所以读操作远远多于写操作。

      秒杀场景下,一般是先读取产品的详情,剩余库存等,查询条件一般是一个简单的产品ID,典型的key-value查询,redis支持高效的key-value查询。

    二、库存同步问题

    商品的库存全部放入redis,库存的读取直接读取redis,到了下单环节,库存的扣除也直接在redis扣除,然后通过消息队列通知后端数据库,最终把库存的扣减异步同步到后台数据库,避免了对数据库的瞬时压力。

    2.1 key-value设置

      1. key:商品id
    
      2. value:字符串-{total : x, ordered : y}。total代表总库存,ordered代表已经扣减的库存。
    

    2.2 库存原子操作

    使用lua脚本

    库存的查询验证和扣减放在一个脚本中,通过lua脚本来保证库存扣减的原子性

    #获取商品库存信息            
    local counts = redis.call("HMGET", KEYS[1], "total", "ordered");
    #将总库存转换为数值
    local total = tonumber(counts[1])
    #将已被秒杀的库存转换为数值
    local ordered = tonumber(counts[2])  
    #如果当前请求的库存量加上已被秒杀的库存量仍然小于总库存量,就可以更新库存         
    if ordered + k <= total then
        #更新已秒杀的库存量
        redis.call("HINCRBY",KEYS[1],"ordered",k)                              
        return k;  
    end               
    return 0
    

    客户端可以通过lua脚本的返回值来判断是否秒杀成功,返回k成功,返回0,则代表失败。

    分布式锁

    通过redis锁,把库存的查询验证和扣减放在一个锁中。同样实现了扣减的原子性。

    //使用商品ID作为key
    key = itemID
    //使用客户端唯一标识作为value
    val = clientUniqueID
    //申请分布式锁,Timeout是超时时间
    lock =acquireLock(key, val, Timeout)
    //当拿到锁后,才能进行库存查验和扣减
    if(lock == True) {
       //库存查验和扣减
       availStock = DECR(key, k)
       //库存已经扣减完了,释放锁,返回秒杀失败
       if (availStock < 0) {
          releaseLock(key, val)
          return error
       }
       //库存扣减成功,释放锁
       else{
         releaseLock(key, val)
         //订单处理
       }
    }
    //没有拿到锁,直接返回
    else
       return
    

    2.3 扣库存的一致性

    上面的设计中,扣减库存在先操作redis中,redis操作成功后发送消息队列,最终通过消息队列实现异步同步数据库库存,实现了最终一致性。 但是有几个异常情况:

    1. redis扣减成功,消息队列发送失败。

      解决方案:

      1. 回滚redis的库存扣减操作。返回下单失败
      2. 另外也可以直接返回扣库存失败,不再操作redis,出现少卖情况。
    2. redis扣减成功,消息也发送成功,但是最终由于网络等原因奔溃

      对于这种情况可以让调用方发起还库存操作。还库存api内部可以通过唯一序列号进行关联(扣和还都传入一样的序列号),来判断是否要真正的还库存。

    2.4 还库存

    还库存先操作数据库,后同步redis,因为秒杀场景还库存的case比较少,直接操作数据库第一时间保证库存的准确性。

    还库存的核心在于保证数据库的库存是准确的。如果后续有部分没有同步到redis,也是部分少买问题,再加上秒杀场景中还库存的case比较少,少买问题可以忽略。

    扣库存是异步传输到db的,所以会出现这种情况:还库存的请求先于扣库存的到达,这时候需要将这个还库存的请求暂时存储,后续不停尝试,比如结果1小时还是没有还成功,可以进行预警人工干预。

    2.5 库存监控

    可以在秒杀结束后,针对实际订单量和库存的扣除量做一个对比监控,用于第一时间发现库存不一致的问题,尤其超卖的case。

    作者:iBrake
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
  • 相关阅读:
    MySQL存储引擎InnoDB与Myisam的六大区别
    PHP+mysql防止SQL注入
    HTTPS 的实现原理
    如何保障 API 接口的安全性?
    使用Merge存储引擎实现MySQL分表
    彻底搞懂Reactor模型和Proactor模型
    REDIS集群脑裂以及解决方案
    linux shell文件合并 去重 分割
    python fnmatch & glob
    sed初理多行合并+sed之G、H、g、h使用+sed n/N使用说明
  • 原文地址:https://www.cnblogs.com/Brake/p/14433964.html
Copyright © 2020-2023  润新知