美文网首页
超卖问题与redis分布式锁

超卖问题与redis分布式锁

作者: kyo1992 | 来源:发表于2021-04-04 08:15 被阅读0次

前言

商品交易是各大互联网交易平台最核心的功能之一,而秒杀系统中的超卖是高并发场景下出现的典型问题,本文把整个交易流程简化为只有一个下单操作,讨论如何如何用redis提供分布式锁解决超卖问题。

背景

电商平台都是以集群模式向外提供服务,每个服务都是由n个节点形成分布式支撑达成高可用和高性能,每个节点上的用户都可以在商品销售时进行下单操作,并发争抢商品这个公共资源,显然是要引入同步机制限制同时只能有一个用户修改商品数量,否则就会出现超卖问题。 而一般而言服务基本都是无状态的,数据是存放在redis集群中,可以直接利用redis实现分布式锁。

代码

一个典型的下单代码流程如下
func deductStock(){
    stock := redisConn.Do("get", "shopId")
    if stock > 0 {
      stock = stock - 1
      redisConn.Do("set", "shopId", stock)
      fmt.Println("success")
    }else{
      fmt.Println("fail")
    }
}
先从redis读取某个商品当前库存值,大于0的话进行扣除,并回写到redis,否则扣除失败。
一般而言,高并发服务每个节点都是多线程同时运行,即使只有一个服务节点运行,在不加锁的情况下直接调用下单函数都会出现超卖问题,更不用说多个服务节点并行调用。

解决方案1

利用redis操作原子性,在下单前先再redis设置标记,设置标记相当于加锁的作用,阻挡其他用户修改库存,
修改代码后如下
func deductStock(){
    result := redisConn.Do("set", "shopIdLock",1,"ex",10,"nx)
    if !result{
      return errcode
    }
    defer redisConn.send("del","shopIdLock")
    stock := redisConn.Do("get", "shopId")
    if stock > 0 {
      stock = stock - 1
      redisConn.Do("set", "shopId", stock)
      fmt.Println("success")
    }else{
      fmt.Println("fail")
    }
}
新增的redis操作代码意思是,假如shopIdLock值不存在,则进行设置,否则返回错误码提示,
设置超时值是为了防止下面逻辑出错导致无法删除shopIdLock的值。
显然,这个redis的值充当了锁的作用,一旦标记被设置,所有其他调用该函数的线程都无法对库存进行操作。

解决方案1可能存在的问题

当下单函数执行得很快,这个方案是没有问题的,但是当下单函数远比本文例子复杂,假如还涉及到一些sql查询操作,用了15s才把下单流程跑完,那么问题就来了,shopIdLock的值在10s后会因为超时被redis删除,此时第二个线程成功设置shopIdLock的值,进行下单操作,这时锁就失效了,再过5s后,第一个线程执行完成,把shopIdLock的值从redis删除,第三个线程成功设置shopIdLock的值,进行下单操作,锁继续失效,到最后就很可能会出现超卖问题。 即使把10s改大点,当你无法判断下单流程时长的时候,问题依然可能存在,而且一旦出现报错,其他购买商品的线程只能等待shopIdLock超时删除了,严重降低并发度。

解决方案2

当某个线程成功设置 锁后,再开辟一个线程定时检查这把锁是否已经解锁,例如设置锁的超时时间为30s,那么定时检查间隔可以设置为 1/3 * 30 = 10s,到达检查点时,发现锁依然存在,则将超时时间延长10s,如果锁不存在则代表完成解锁,定时任务可以释放,线程关闭。

提高锁的并发性能

假如有1w个用户并发下单,到达redis处为了加锁都变成了串行模式,并发变成串行。那么为了提高并发度,可以将锁进行分段。例如锁的key为shopIdLock,将锁分成10段,key依次为
shopIdLock_1 ~ shopIdLock10,将可售商品数量也分成10份分配到每把锁上,当用户尝试下单时,用均衡算法从10把锁中选1把进行加锁,理论上并发度提高了10倍,同理将锁分成100段就可以提高100倍并发度。

相关文章

网友评论

      本文标题:超卖问题与redis分布式锁

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