美文网首页
Redis实现分布式锁

Redis实现分布式锁

作者: cbhe | 来源:发表于2020-06-17 17:31 被阅读0次

    前言

    一般有三种方式来实现分布式锁:

    1. 使用数据库实现
    2. 使用 redis 实现
    3. 使用 zookeeper 实现

    本文就是来分析一下如何用 redis 实现分布式锁。

    实现分布式锁要遵循的原则

    为了保证分布式锁的可用性和可靠性,至少要满足以下四条原则:

    1. 互斥性:任意时刻,必须保证只能有一个客户端可以获取到锁。
    2. 解铃还须系铃人:分布式锁只能由加锁的客户端来解锁。
    3. 不会发生死锁:即使持有锁的客户端崩溃,也要保证分布式锁在可容忍的时间内解锁。
    4. 具有容错性:只要大部分 redis 客户端正常运行,分布式锁就可以正常加锁和解锁。

    加锁方式

    首先我们看一下Redis的这个方法:

    SET key value [EX seconds|PX milliseconds] [NX|XX] [KEEPTTL]
    

    它的参数解释如下:

    • EX seconds:以秒为单位设置过期时间
    • PX milliseconds:以毫秒为单位设置过期时间
    • NX:仅当 key 不存在时添加 key:value
    • XX:仅当 key 已经存在时执行,并将原值改为命令中指定的 value
    • KEEPTTL:保留先前给这个 key 设置的过期时间(这个怎么用,求大神留言区解答一下)

    它的返回值如下:

    • 设置成功返回 “OK”
    • 如果失败则返回 Null (失败一般是因为不满足 NX|XX 条件)

    而分布式锁加锁过程就利用了上面我们提到的 SET 函数,java 代码如下:

    String lockResult = jedis.set(lockName, clientId, "NX", "EX", expireTime);
    

    其中变量lockName就是分布式锁的名字,clientId是当前请求锁的客户端的id。后面三个参数的意思就是只有当指定键不存在时才执行添加操作,且过期时间是expireTime指定的秒数。如果该条语句返回"OK"则表示加锁成功,返回 null 表示加锁失败。

    我们再分析一下这个加锁命令是否满足了可靠性和可用性:

    • 互斥性:对于指定的一个分布式锁 lockName,由于“NX”下只能在 key 不存在时才能设置成功,因此多台客户端时显然只能有一个客户端才能设置成功。
    • 只能由加锁的客户端执行解锁:我们在加锁时制定了 value 为客户端id,因此解锁时可以根据该id来判断是否是本客户端加的锁,所以可以保证只能是加锁的客户端才能执行解锁操作。
    • 不会发生死锁:我们指定了过期时间,因此即便客户端不执行解锁操作,在指定时间后也会删除该 key,确保了该分布式锁能在容忍的时间内自动解锁。

    解锁操作

    解锁操作相对简单,我们可以先查询一下lockName的值,如果能查到,证明已经加锁。再比较一下查到的clientId是否为当前客户端id,如果是,则执行删除操作,删除这个 key,就可以解锁了。如果查不到则无需做任何操作,或者报错(这时候程序员认为有加锁但实际上没有,肯定是哪里出了问题)。
    解锁代码如下:

    public void unLock(String lockName, int clientId)
        Stirng lockedClientId = jedis.get(lockName);
        if (lockedClientId != null && lockedClientId.equals(clientId)){
            jedis.del(lockName);
        }
    }
    

    编程模板

    上面说的解锁方法在某种情况下可以简化一些,一般我们在编程时候通用加锁解锁模型如下:

    public void lockAndHandle(){
        String lockResult = jedis.set(lockName, clientId, "NX", "EX", expireTime);
        if(lockResult == null || !lockResult.equals("OK")){
            return;
        }
    
        try{
            doSomethings();
        } catch(Exception e){
            handleException(e);
        } finally {
            jedis.del(lockName);
        }
    }
    

    相关文章

      网友评论

          本文标题:Redis实现分布式锁

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