什么是分布式锁及正确使用redis实现分布式锁

分布式锁

  分布式锁其实可以理解为:控制分布式系统有序的去对共享资源进行操作,通过互斥来保持一致性。 举个不太恰当的例子:假设共享的资源就是一个房子,里面有各种书,分布式系统就是要进屋看书的人,分布式锁就是保证这个房子只有一个门并且一次只有一个人可以进,而且门只有一把钥匙。然后许多人要去看书,可以,排队,第一个人拿着钥匙把门打开进屋看书并且把门锁上,然后第二个人没有钥匙,那就等着,等第一个出来,然后你在拿着钥匙进去,然后就是以此类推

实现原理

  1. 互斥性

    保证同一时间只有一个客户端可以拿到锁,也就是可以对共享资源进行操作

  2. 安全性

    只有加锁的服务才能有解锁权限,也就是不能让a加的锁,bcd都可以解锁,如果都能解锁那分布式锁就没啥意义了

    可能出现的情况就是a去查询发现持有锁,就在准备解锁,这时候忽然a持有的锁过期了,然后b去获得锁,因为a锁过期,b拿到锁,这时候a继续执行第二步进行解锁如果不加校验,就将b持有的锁就给删除了

  3. 避免死锁

    出现死锁就会导致后续的任何服务都拿不到锁,不能再对共享资源进行任何操作了

  4. 保证加锁与解锁操作是原子性操作

    例:

    假设a用redis实现分布式锁,

    1,设置key set(key,value)

    2,给key设置过期时间

    假设现在a刚实现set后,程序崩了就导致了没给key设置过期时间就导致key一直存在就发生了死锁

如何实现分布式锁

  实现分布式锁的方式有很多,只要满足上述条件的都可以实现分布式锁,比如数据库,redis,zookeeper,在这里就先讲一下如何使用redis实现分布式锁

使用redis实现分布式锁

步骤核心:

  1. 使用redis命令 set key value NX EX max-lock-time实现加锁
  2. 使用redis命令 EVAL 实现解锁

需要满足分布式锁的实现原理

加锁:
   Jedis jedis = new Jedis("127.0.0.1", 6379);
   private static final String SUCCESS = "OK";

   /**
    * 加锁操作
    * @param key 锁标识
    * @param value 客户端标识
    * @param timeOut 过期时间
    */
   public Boolean lock(String key,String value,Long timeOut){
       String var1 = jedis.set(key,value,"NX","EX",timeOut);
       if(LOCK_SUCCESS.equals(var1)){
           return true;
       }
       return false;
   }

解读

  1. 加锁操作:jedis.set(key,value,"NX","EX",timeOut),保证加锁的原子操作
  2. key就是rediskey 大专栏  什么是分布式锁及正确使用redis实现分布式锁值作为锁的标识,value在这里作为客户端的标识,只有key-value都比配才有删除锁的权利,保证安全性
  3. 通过timeOut设置过期时间保证不会出现死锁,避免死锁
  4. NXEX什么意思

    NX:只有这个key不存才的时候才会进行操作,if not exists

    EX:设置key的过期时间为秒,具体时间由第5个参数决定

解锁
   Jedis jedis = new Jedis("127.0.0.1", 6379);
   private static final Long UNLOCK_SUCCESS = 1L;

   /**
    * 解锁操作
    * @param key 锁标识
    * @param value 客户端标识
    * @return
    */
   public static Boolean unLock(String key,String value){
       String luaScript = "if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else  return 0 end";
       Object var2 = jedis.eval(luaScript,Collections.singletonList(key), Collections.singletonList(value));
       if (UNLOCK_SUCCESS == var2) {
           return true;
       }
       return false;
   }

解读

  1. luaScript 这个字符串是个lua脚本,代表的意思是如果根据key拿到的value跟传入的value相同就执行del,否则就返回0,保证安全性
  2. jedis.eval(String,list,list);这个命令就是去执行lua脚本,KEYS的集合就是第二个参数,ARGV的集合就是第三参数【保证解锁的原子操作】

? 上述就实现了怎么使用redis去正确的实现分布式锁,但是有个小缺陷就是锁过期时间要设置为多少合适,这个其实还是需要去根据业务场景考量一下的

重试机制

  上面那只是讲了加锁与解锁的操作,试想一下如果在业务中去拿锁如果没有拿到是应该阻塞着一直等待还是直接返回,这个问题其实可以写一个重试机制,根据重试次数和重试时间做一个循环去拿锁,当然这个重试的次数和时间设多少合适,是需要根据自身业务去衡量的

    /**
    * 重试机制
    * @param key 锁标识
    * @param value 客户端标识
    * @param timeOut 过期时间
    * @param retry 重试次数 不要太大
    * @param sleepTime 重试间隔时间
    * @return
    */
   public Boolean lockRetry(String key,String value,Long timeOut,Integer retry,Long sleepTime){
       Boolean flag = false;
       try {
           for (int i=0;i<retry;i++){
               flag = lock(key,value,timeOut);
               if(flag){
                   break;
               }
               Thread.sleep(sleepTime);
           }
       }catch (Exception e){
           e.printStackTrace();
       }
       return flag;
   }

redis实现分布式锁就写完了,下次用zookeeper去实现分布式锁

文中set命令详解:http://redisdoc.com/string/set.html

原文地址:https://www.cnblogs.com/lijianming180/p/12276053.html

时间: 2024-10-06 08:00:52

什么是分布式锁及正确使用redis实现分布式锁的相关文章

分布式锁(2) ----- 基于redis的分布式锁

Redis单机版实现 set和lua实现 获取锁 SET resource_name my_random_value NX PX 30000 NX key不存在时才set PX 设置过期时间 my_random_value 要保证每台客户端的每个锁请求唯一,可以使用UUID+ThreadID 该命令在Redis 2.6.12才有,网上有基于setnx.epire的实现和基于setnx.get.getset的实现,这些多多少少都有点瑕疵,大概率是旧版本的redis实现,建议高版本的redis还是使

Redis分布式锁的正确实现方式

前言 分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上已经有各种介绍Redis分布式锁实现的博客,然而他们的实现却有着各种各样的问题,为了避免误人子弟,本篇博客将详细介绍如何正确地实现Redis分布式锁. 可靠性 首先,为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件: 互斥性.在任意时刻,只有一个客户端能持有锁. 不会发生死锁.即使有一个客户端在

Redis分布式锁的正确实现方式(Java版)

前言 分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上已经有各种介绍Redis分布式锁实现的博客,然而他们的实现却有着各种各样的问题,为了避免误人子弟,本篇博客将详细介绍如何正确地实现Redis分布式锁. 可靠性 首先,为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件: 互斥性.在任意时刻,只有一个客户端能持有锁. 不会发生死锁.即使有一个客户端在

80% 人不知道的 Redis 分布式锁的正确实现方式(Java 版)

本博客使用第三方开源组件Jedis实现Redis客户端,且只考虑Redis服务端单机部署的场景. 前言 分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上已经有各种介绍Redis分布式锁实现的博客,然而他们的实现却有着各种各样的问题,为了避免误人子弟,本篇博客将详细介绍如何正确地实现Redis分布式锁. 可靠性 首先,为了确保分布式锁可用,我们至少要确保锁的实现同时

Redis 分布式锁的正确打开方式

前言 分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上已经有各种介绍Redis分布式锁实现的博客,然而他们的实现却有着各种各样的问题,为了避免误人子弟,本篇博客将详细介绍如何正确地实现Redis分布式锁. 可靠性 首先,为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件: 互斥性.在任意时刻,只有一个客户端能持有锁. 不会发生死锁.即使有一个客户端在

Redis(十三):Redis分布式锁的正确实现方式

前言 分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上已经有各种介绍Redis分布式锁实现的博客,然而他们的实现却有着各种各样的问题,为了避免误人子弟,本篇博客将详细介绍如何正确地实现Redis分布式锁. 可靠性 首先,为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件: 互斥性.在任意时刻,只有一个客户端能持有锁. 不会发生死锁.即使有一个客户端在

如何优雅地用Redis实现分布式锁

https://mp.weixin.qq.com/s?__biz=MzAxNjM2MTk0Ng==&mid=2247484976&idx=2&sn=a0b6771f0b4e471c710f8cd51c243971&chksm=9bf4b685ac833f936f3722a795ae202a3be37a3fb57332393e2eec3bbf8b34c4705d5b14a964&mpshare=1&scene=1&srcid=0919f7t2duWiu

关于redis实现分布式锁

前言 分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上已经有各种介绍Redis分布式锁实现的博客,然而他们的实现却有着各种各样的问题,为了避免误人子弟,本篇博客将详细介绍如何正确地实现Redis分布式锁. 可靠性 首先,为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件: 互斥性.在任意时刻,只有一个客户端能持有锁. 不会发生死锁.即使有一个客户端在

[Redis] 基于redis的分布式锁

前言分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁. 可靠性首先,为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件: 互斥性.在任意时刻,只有一个客户端能持有锁.不会发生死锁.即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁.具有容错性.只要大部分的Redis节点正常运行,客户端就可以加锁和解锁.解铃还须系铃人.加锁和解锁必须