Redis实现分布式锁

2020-06-17  本文已影响0人  cbhe

前言

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

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

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

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

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

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

加锁方式

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

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

它的参数解释如下:

它的返回值如下:

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

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

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

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

解锁操作

解锁操作相对简单,我们可以先查询一下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);
    }
}
上一篇下一篇

猜你喜欢

热点阅读