美文网首页
【redis】分布式锁的实现

【redis】分布式锁的实现

作者: 何笙 | 来源:发表于2019-05-29 18:25 被阅读0次

    setnx

    redis 分布式锁使用非常广泛的,来实现对一些共享资源进行互斥访问。

    一般使用setnx(set if not exists) 来抢占,del 来释放。

     setnx lock 1
    ... do something ...
    del lock
    

    但是这个流程有问题,如果del 调用失败或者异常导致del 没有调用,就会陷入死锁,导致锁永远不能释放。可以想到的一个解决方案就是我们给锁加一个过期时间,如下:

     setnx lock 1
    expire lock 5
    ... do something ...
    del lock
    

    但是上述流程还是有问题,如果expire 调用失败,还是会陷入死锁。于是我们想到下面代码程序校验的方案来解决可能的死锁问题:

    current_ts = time()
    lock_ts = redis->get('lock')
    if (!lock_ts || current_ts - lock_ts > 5) {
        redis->set(lock, current_ts)
        redis->expire(lock, 5)
        ... do something ...
        del lock
    } else {
      return false
    }
    

    但是上述方案还是有问题的,原因就在于抢占资源不是原子操作的,当我们的程序访问并发比较高的时候,会有多个访问同时抢占到锁,不能保证互斥性。

    那怎么解决呢? 好在Redis 2.8 版本之后,redis 的set 执行增加了扩展参数,使setnx 和expire 可以一起执行,从而解决了上述难题。

    SET KEY VALUE [EX seconds] [PX milliseconds] [NX|XX]

    set lock 1 ex 5 nx 
    ... do something ...
    del lock
    

    del误删

    上述方案还是会存在误删问题:假如线程A获得锁并且设置超时时间为30秒,某种原因导致线程A执行很慢,超过30秒后线程A锁自动过期,释放了锁,线程B获了锁。随后线程A执行完成,del删除锁,但线程B还未执行完成,实际上线程A删除的是线程B的锁。

    解决方案就是通过lua脚本实现一个乐观锁:
    线程在加锁的时候,可以给锁加一个版本号或者随机数来区分。

    SET lock rand_value EX 300 NX
    

    del锁之前先做判断,通过lua脚本来保证判断和del 两个操作的原子性。

    if redis.call("get",KEYS[1]) == ARGV[1] then
        return redis.call("del",KEYS[1])
    else
        return 0
    end
    

    其他问题

    1. 前面这个算法中出现的锁的有效时间(lock validity time),设置成多少合适呢?如果设置太短的话,锁就有可能在客户端完成对于共享资源的访问之前过期,从而失去保护;如果设置太长的话,一旦某个持有锁的客户端释放锁失败,那么就会导致所有其它客户端都无法获取锁,从而长时间内无法正常工作。看来真是个两难的问题。

    2. 假如Redis节点宕机了,那么所有客户端就都无法获得锁了,服务变得不可用。为了提高可用性,我们可以给这个Redis节点挂一个Slave,当Master节点不可用的时候,系统自动切到Slave上(failover)。但由于Redis的主从复制(replication)是异步的,这可能导致在failover过程中丧失锁的安全性。这是基于单Redis节点的分布式锁无法解决的,而Redlock算法就是为了解决这个问题提出的,是基于多个Redis节点(都是Master)的一种实现。详情可以看下官网介绍:https://redis.io/topics/distlock ,也可以参考一下这篇blog的介绍:http://zhangtielei.com/posts/blog-redlock-reasoning.html

    相关文章

      网友评论

          本文标题:【redis】分布式锁的实现

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