美文网首页
redis如何实现分布式锁以及存在的问题

redis如何实现分布式锁以及存在的问题

作者: yfsheng | 来源:发表于2019-09-27 10:34 被阅读0次

Redis 分布式锁

    传统setnx 方法(先 status = setnx(key ,valiue) 判断status 是否为 1 ,如果为1 表示占用锁成功 ,占用锁成功后 再设置失效时间  )  存在的问题  setnx 和expire 不是原子操作,如果expire 失败了  就没有失效时间,当前线程会一直占用锁不释放。所以需要对 setnx 和 expire 操作保证原子性 。

     从 Redis 2.6.12 版本开始,SET命令的行为可以通过一系列参数来修改:

EX second :设置键的过期时间为 second 秒。 SET key value EX second 效果等同于 SETEX key second value 。

PX millisecond :设置键的过期时间为 millisecond 毫秒。 SET key value PX millisecond 效果等同于 PSETEX keymillisecond value 。

NX :只在键不存在时,才对键进行设置操作。 SET key value NX 效果等同于 SETNX key value 。

XX :只在键已经存在时,才对键进行设置操作。

 redisCluster.set(key,"1","nx","ex",100);  //如果 key 不存在的话 往缓存中存 value =1 存100秒 ,设置成功以后 返回“OK”

     这种方法可以保证获取锁的原子性 不会因为某个线程获取锁时redis 挂掉等导致的长期占有锁的情况。目前笔者的部门采用的就是此种方法。但是此方法仍然存在一定的问题。

     假设一个线程持有锁的有效时间是10秒钟,但是由于业务复杂 在持有锁期间线程1 的任务没有执行完毕 ,此时锁已经开放,线程2 可以持有锁,当线程1 任务执行完毕以后会进行del(key)释放锁的操作,但是此时释放的锁确实线程2 持有的锁,此时就会有问题。

    所以可以在释放锁(del)之前进行判断一下 持有锁的线程是不是 当前的线程。

    加锁的时候  StringthreadId = Thread.currentThread().getId()  set(key,threaId,nx,ex,100);

    释放锁的时候 if(threadId .equals(redisClient.get(key))){del(key)}

    但是由于 判断的操作不是原子性操作,仍然有上述的原子性问题,所以要想实现验证和删除过程的原子性,可以使用Lua脚本来实现。这样就能保证验证和删除过程的正确性了。

    如果担心锁要失效的时候 任务没有执行完毕,我们可以让获得锁的线程开启一个守护线程,用来给快要过期的锁“续航”。

    事实上这类琐最大的缺点就是它加锁时只作用在一个Redis节点上,即使Redis通过sentinel保证高可用,如果这个master节点由于某些原因发生了主从切换,那么就会出现锁丢失的情况:

在Redis的master节点上拿到了锁;

但是这个加锁的key还没有同步到slave节点;

master故障,发生故障转移,slave节点升级为master节点;

导致锁丢失。

    正因为如此,Redis作者antirez基于分布式环境下提出了一种更高级的分布式锁的实现方式:Redlock

Redlock原理后续会继续讨论。

相关文章

网友评论

      本文标题:redis如何实现分布式锁以及存在的问题

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