Redis分布式锁的正确思路及踩坑详解

本文介绍Redis分布式锁的实现正确思路以及中间会遇到的坑 

 

一.v1版本

setNX命令可以用于加锁判断,对于同一个key,如果已存在,则未false,不存在则返回true,表示加锁成功。那么假设在并发场景下,同一时间假设30个请求打进来,会有29个return返回,只有1个会执行业务代码,这里依靠的是redis的单线程模型,不论你的并发,在redis的单线程模型里永远都会排队依次执行,同一时间只会执行一条指令。

result := redis.setNX(lockkey,value)
if !result {
    return "系统繁忙中,请稍后再试"
}

//业务代码 下单逻辑等

redis.delete(localkey)

坑点1:假设业务代码抛异常,导致程序奔溃,或者web服务宕机,redis的delete指令不会执行,导致死锁

解决方案,redis设置超时时间,测试业务代码时间,一般超时时间为业务代码时间加5s左右.为什么不加多一点,因为分布式锁往往应用于高并发场景,例如秒杀,你想想在双十一如果因为死锁,而你设置了超时时间会10分钟,那损失可不是一丁点,对于用户而言也一点都不友好

二.v2版本

result := redis.setNX(lockkey,value)
if !result {
    return "系统繁忙中,请稍后再试"
}
// 加超时时间
redis.expire(lockkey,10s) 

//业务代码 下单逻辑等

redis.delete(localkey)

坑点2:setNX和expire为两条指令,非原子性,如果在两条指令中间系统宕机,同样死锁。

你可能会说,redis的速度很快,两条指令之间的时间可忽略不计。那就大错特错了,因为一个系统有同时肯定有很有请求,万一在两条指令之间隔着一条keys*的指令,那可能会消耗十几秒时间,同样会有问题。

解决思路,redis的set指令可以同时实现这两条指令

三,v3版本

result := redis.set(lockkey,value,10s,"nx")
if !result {
    return "系统繁忙中,请稍后再试"
}

//业务代码 下单逻辑等
redis.delete(localkey)

此版本在一般场景下确实没有问题,实际上已经满足大多数中小型公司的需求,然而在真正的高并发场景里依然存在bug

坑点3:因为你设置的过期时间为10s,在高并发场景下,系统负载升高,假设业务逻辑执行时间变为11s,那么10s后,会解锁,第2个请求则会拿到锁执行业务逻辑,此时1s后第1个请求会解锁,解掉第2个请求的锁,同样的,第2个请求也会解锁其他请求的锁,在真正高并发场景下,这把锁可能就此永远失效

解决思路:为每把锁设置唯一值

四.v4版本

value := uuid.random()
result := redis.set(lockkey,value,10s,"nx")
if !result {
    return "系统繁忙中,请稍后再试"
}

//业务代码 下单逻辑等
if redis.get("lockkey") == value{
    redis.delete(localkey)
}

你可能已经发现解锁命令又失去了原子性,与刚才的问题一样,那怎么办呢,redis的delete的命令并没有此类参数呀,这里需要用lua脚本扩展

五.v5版本

value := uuid.random()
result := redis.set(lockkey,value,10s,"nx")
if !result {
    return "系统繁忙中,请稍后再试"
}

//业务代码 下单逻辑等

lua.getAndDelete(lockkey,value)

然而还是没有解决业务逻辑为11s时,超时时间为10s,那么锁将提前失效.

解决方案:在加锁同时,起一个子线程定时扫描,用ttl命令获取lockkey的剩余时间,如果大于0,则往上再加一定时间,不如说三分之一的超时时间。

程序写到这里,大部分场景已经没有问题。然而如果redis是集群环境,而当你的master结点宕机的时候,从机失去这把锁,导致超卖等现象的产生。针对这问题,redis的作者提供了一种思路叫红锁,redLock,即例如设置5把锁,能拿到一半以上的锁时即为成功,需注意的是5把锁的lockkey一定要不同,这样在集群环境,5把锁才会分片到不同的节点,如果key相同,则会分片到同一节点,那么将毫无意义。

细心的同学可能会发现,最终实现的版本即为Redission框架,以上即为此框架的设计思路。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值