redis分布式锁实现原理_redis分布式锁实现方案解读

今天读了redis分布式锁的相关内容,刚才写了很多,结果发布的时候,出了bug,正文内容全没了,重新写一下。

目前redis因其优良的性能及多种数据结构的支持,已经被越来越多的公司使用,其中一些高并发场景,如抢购、秒杀、抢票等,会用到redis来实现分布式锁及减库存操作,但是,在使用过程中,可能出现一些超卖的情况,这些基本上是由于redis分布式锁实现方式及减库存操作非原子性引起的,在此分析下常见的分布式锁实现方案。

1.redis setnex实现分布式锁。

1.1 目前,redis已经支持set(key,val,ex|px,nx|xx)来保证原子性,但是在低版本(2.6.12以前),只能通过setnx和setex两条命令来实现,是非原子性操作(如果一个进程在setnx以后panic了,那这个锁就永远不能unlock了),具体可参考SET - Redis 命令参考。常见的加锁方式如下:

//10s nx加锁
set(k,v,100s,nx)
//业务逻辑
...
//解锁
unlock

1.2 这种情况下,只关心k是否已经存在,不关心val的值。但是,在高并发模式下,这种方式会出现问题。比如,秒杀时,负载极高时,可能出现进程A业务逻辑处理时间超过10s的情况,这种情况下,进程A还在处理业务逻辑时,锁已经失效了。此时进程B可以获取到锁,并处理业务,如果在处理过程中,A走到了解锁逻辑,就会把B进程的锁unlock掉。这样逐级往后,会出现套娃情况,后果无法预料。

1.3 因此做优化,来保证进程A只能unlock掉自己的锁。可以启用val,比较不同进程的val值,与set值相同,才可以unlock,此时需要保证不同进程获取到的val不一样,可以用各种开源的getuuid算法。伪代码如下:

//10s nx加锁
set(k,v,100s,nx)
//业务逻辑
...
//解锁
if(val == get(k)){
   unlock 
}

但是这样,解锁还不是原子操作,为了保证原子操作,可以用redis的lua脚本功能,用lua脚本的原子性来保证解锁的原子性。得解。但是有一个最大的问题,它加锁时只作用在一个Redis节点上,即使Redis通过sentinel保证高可用,如果这个master节点由于某些原因发生了主从切换,那么就会出现锁丢失(主要表现情况是:redis在master加锁了,还未来得及同步到slave,发生了主从切换,导致锁丢失,这种情况下此种实现方式无解,详细参考3.redlock)。

2.redisson:Java实现的对各种redis的各种功能封装,包括分布式锁,红锁,哨兵、主从等,原子操作主要是通过lua脚本来实现的,感兴趣的同学可以去看下源码。具体功能可以参考redisson官网:https://redisson.org/。

3.redlock(红锁):这是一个大神写的,主要用于redis多实例分布式锁,其实现依赖于redis的多个无关联的实例。只要n/2+1个实例顺序加锁成功(超过设置的超时时间视为失败),并且加锁时间和<ex时间,即视为加锁成功。比如现有5个实例,加锁100s,如果有3个实例依次加锁成功,并且加锁总时间<100s时,加锁成功;否则加锁失败(参考:https://redis.io/topics/distlock)。

最后总结下,如果并发量不大,可以考虑1.1的方式;如果高并发,则可以考虑1.2和1.3的方案;如果对加锁有极致的要求,则可以考虑redlock。

以上。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值