redis锁

作者: 帮我的鸵鸟盖个章 | 来源:发表于2021-09-08 16:16 被阅读0次

redis分布式锁的实现,本质是使用setnx 【set if not exits】命令设置key,设置成功则加锁成功,未设置成功,说明已被其他线程设置,加锁失败。setnx命令需要与 expire命令一起使用,给锁加一个过期时间,这样锁就可以释放,不会因为服务器宕机而造成死锁。

这里出现以下几个问题:

  • setnxexpire两个操作中间的时间,redis服务器挂了,造成死锁
  • 如何保证客户端获取锁后,不被其他客户端解锁。
  • 如何保证锁的过期时间不被其他客户端修改。

加锁代码

public class RedisTool {
 
    private static final String LOCK_SUCCESS = "OK";
    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";
 
    /**
     * 尝试获取分布式锁
     * @param jedis Redis客户端
     * @param lockKey 锁
     * @param requestId 请求标识
     * @param expireTime 超期时间
     * @return 是否获取成功
     */
    public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
        String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
 
        if (LOCK_SUCCESS.equals(result)) {
            return true;
        }
        return false;
 
    }
}

可以看到,我们加锁就一行代码:jedis.set(String key, String value, String nxxx, String expx, int time),这个set()方法一共有五个形参:

  • 第一个为key,我们使用key来当锁,因为key是唯一的。
  • 第二个为value,我们传的是requestId,很多童鞋可能不明白,有key作为锁不就够了吗,为什么还要用到value?原因就是我们在上面讲到可靠性时,分布式锁要满足第四个条件解铃还须系铃人,通过给value赋值为requestId,我们就知道这把锁是哪个请求加的了,在解锁的时候就可以有依据。requestId可以使用UUID.randomUUID().toString()方法生成。
  • 第三个为nxxx,这个参数我们填的是NX,意思是SET IF NOT EXIST,即当key不存在时,我们进行set操作;若key已经存在,则不做任何操作;
  • 第四个为expx,这个参数我们传的是PX,意思是我们要给这个key加一个过期的设置,具体时间由第五个参数决定。
  • 第五个为time,与第四个参数相呼应,代表key的过期时间。

解锁代码

public class RedisTool {
 
    private static final Long RELEASE_SUCCESS = 1L;
 
    /**
     * 释放分布式锁
     * @param jedis Redis客户端
     * @param lockKey 锁
     * @param requestId 请求标识
     * @return 是否释放成功
     */
    public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {
 
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
 
        if (RELEASE_SUCCESS.equals(result)) {
            return true;
        }
        return false;
 
    }
}
  • 写一个lua脚本代码,redis需要执行的命令
  • 使用jedis.eval()方法,将lua脚本命令交给redis服务端执行

以上代码,只能保证单机的情况下,锁是OK的。如果是分布式,那么就要用到封装好的redisson框架。

redisson使用了Lua命令。

锁的内容: hash的名称为锁名,hash里面内容仅包含一条键值对,键为redisson客户端唯一标识+持有锁线程id,值为锁重入计数;给hash设置的过期时间就是锁的过期时间。

发布订阅模式。发布订阅即为,如果AB同时去争抢一把锁,A成功B失败,那么B可以订阅这把锁的信息。当A解锁之后,对锁的信息进行发布,B得到通知后继续去争抢这把锁。

使用了watchDog看门狗,这个watchDog实际是一个回调,会每隔十秒去执行一次,如果锁仍存在,那么增加锁的过期时间。

redis红锁

需要特别注意的是,RedissonLock 同样没有解决 节点挂掉的时候,存在丢失锁的风险的问题。而现实情况是有一些场景无法容忍的,所以 Redisson 提供了实现了redlock算法的 RedissonRedLock,RedissonRedLock 真正解决了单点失败的问题,代价是需要额外的为 RedissonRedLock 搭建Redis环境。

相关文章

  • 秒杀随笔

    方法: mysql悲观锁 mysql乐观锁 PHP+redis分布式锁 PHP+redis乐观锁(redis wa...

  • 大佬浅谈分布式锁

    redis 实现 redis 分布锁一、redis 实现分布式锁(可重入锁)redission 实现分布式锁1、对...

  • Redis实现分布式锁

    Redis实现分布式锁 一、Redis单节点实现 (一) 获取锁 使用 Redis 客户端获取锁,向Redis发出...

  • 分布式锁之redis-lua脚本

    目录 redis分布式锁,Lua,Lua脚本,lua redis,redis lua 分布式锁,redis set...

  • redis锁

    1、单节点的redis锁优点:快缺点:不安全(redis节点断电,网络不通,锁过期等等) 2、多节点redis锁,...

  • Redis分布式锁

    Redis分布式锁 实现 Redis 锁主要利用 Redis 的 setnx 命令。 加锁命令:SETNX key...

  • 死磕 java同步系列之redis分布式锁进化史

    问题 (1)redis如何实现分布式锁? (2)redis分布式锁有哪些优点? (3)redis分布式锁有哪些缺点...

  • 使用JVM提高秒杀系统性能

    前提 使用redis分布式锁,解决秒杀系统库存为零 继续扣减问题 redis分布式锁出现的问题 使用redis锁,...

  • Zookeeper实现分布式锁(一)While版

    前面文章讲解了用Redis实现分布式锁的方式: 分布式锁之Redis实现(acquire)分布式锁之Redis实现...

  • 基于redis实现的分布式锁

    本文要点 基于redis实现分布式锁demo 基于redis实现分布式锁原理 基于redis实现分布式锁优缺点 正...

网友评论

      本文标题:redis锁

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