什麼是分佈式鎖

本文:chenmingyu.top/redis-lock/html

分佈式鎖

  分佈式鎖其實能夠理解爲:控制分佈式系統有序的去對共享資源進行操做,經過互斥來保持一致性。 舉個不太恰當的例子:假設共享的資源就是一個房子,裏面有各類書,分佈式系統就是要進屋看書的人,分佈式鎖就是保證這個房子只有一個門而且一次只有一我的能夠進,並且門只有一把鑰匙。而後許多人要去看書,能夠,排隊,第一我的拿着鑰匙把門打開進屋看書而且把門鎖上,而後第二我的沒有鑰匙,那就等着,等第一個出來,而後你在拿着鑰匙進去,而後就是以此類推java

實現原理

  1. 互斥性redis

    保證同一時間只有一個客戶端能夠拿到鎖,也就是能夠對共享資源進行操做數據庫

  2. 安全性安全

    只有加鎖的服務纔能有解鎖權限,也就是不能讓a加的鎖,bcd均可以解鎖,若是都能解鎖那分佈式鎖就沒啥意義了分佈式

    可能出現的狀況就是a去查詢發現持有鎖,就在準備解鎖,這時候突然a持有的鎖過時了,而後b去得到鎖,由於a鎖過時,b拿到鎖,這時候a繼續執行第二步進行解鎖若是不加校驗,就將b持有的鎖就給刪除了lua

  3. 避免死鎖spa

    出現死鎖就會致使後續的任何服務都拿不到鎖,不能再對共享資源進行任何操做了code

  4. 保證加鎖與解鎖操做是原子性操做htm

    例:

    假設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值做爲鎖的標識,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實現分佈式鎖就寫完了

文中set命令詳解:redisdoc.com/string/set.…

相關文章
相關標籤/搜索