中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

RedisTemplate下Redis分布式鎖引發的系列問題有哪些

發布時間:2021-03-24 13:38:09 來源:億速云 閱讀:270 作者:小新 欄目:開發技術

這篇文章主要介紹了RedisTemplate下Redis分布式鎖引發的系列問題有哪些,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

     首先,分布式集群的問題大家都知道,會造成數據庫的插入重復問題,會造成一系列的并發性問題。

     解決的方式呢也大概如下幾點,百度以及谷歌上都能搜到的解決方式:

     1:數據庫添加唯一索引

     2:設計接口冪等性

     3:依靠中間件使用分布式鎖,而分布式鎖又分為Redis和Zookeeper

    由于Zookeeper我沒怎么接觸過,并且我項目中本來就引用了Redis,所以就想著用Redis來做分布式鎖,也高端洋氣上檔次點。

    首先基于Redis的操作,我們必須要保證其原子性,也就是要么全部成功,要么全部失敗,先從Redis的客戶端入手。

    就Redis客戶端而言,我們通過的操作是先使用setnx指令,如果成功則返回1,失敗則返回0

RedisTemplate下Redis分布式鎖引發的系列問題有哪些

   可是就分布鎖鎖而言,一個常用的問題就是如果一個服務setnx成功了,但是在解鎖的時候如果發生了宕機或者一些特殊因素,導致無法解鎖,那么其他服務將陷入死鎖的狀態。所以,我們在用 setnx 的同時想著去用 expire 指令對鎖進行一個過期操作

 RedisTemplate下Redis分布式鎖引發的系列問題有哪些

   從指令可以看出 setnx 和 expire 指令是分開的,如果在這中間的空隙過程中如果有特殊因素導致指令無法繼續,也會導致死鎖的產生。

以下參考自老錢的 Redis 深度歷險:核心原理與應用實踐

   為了解決這個疑難,Redis 開源社區涌現了一堆分布式鎖的 library,專門用來解決這個問題。實現方法極為復雜,小白用戶一般要費很大的精力才可以搞懂。如果你需要使用分布式鎖,意味著你不能僅僅使用 Jedis 或者 redis-py 就行了,還得引入分布式鎖的 library。

  為了治理這個亂象,Redis 2.8 版本中作者加入了 set 指令的擴展參數,使得 setnx 和 expire 指令可以一起執行,徹底解決了分布式鎖的亂象。從此以后所有的第三方分布式鎖 library 可以休息了。

   RedisTemplate下Redis分布式鎖引發的系列問題有哪些

  以上都是基于Redis的操作,但是我們在JAVA中如何去運用分布式鎖呢。

  首先在Redis方面我用的是RedisTemplate對Redis進行操作的 ,而RedisTemplate在目前情況下如果不借助于是無法保證其原子性的,所以我們需要借助于Redis的Lua腳本。

   先上Lua腳本的代碼

// 加鎖
if 
    redis.call('setNx',KEYS[1],ARGV[1]) 
  then 
    if redis.call('get',KEYS[1])==ARGV[1] 
    return redis.call('expire',KEYS[1],ARGV[2]) 
  else 
    return 0 
  end 
end
 
// 解鎖
  redis.call('get', KEYS[1]) == ARGV[1] 
then 
  return redis.call('del', KEYS[1]) 
else 
  return 0

    Java調用腳本有兩種方式

   1。新建一個腳本文件,在代碼中調用其絕對路徑地址

     redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource(地址)));

   2。在Java代碼中以字符串的方式傳入

    redisScript.setScriptText(腳本);

  我是用的第二種方式實現的,下面是JAVA代碼

/**
   * 獲取鎖
   * @param lockKey
   * @param value
   * @param expireTime:單位-秒
   * @return
   */
  public boolean getLock(String lockKey, String value, int expireTime){
    boolean ret = false;
    try{
      String script = "if redis.call('setNx',KEYS[1],ARGV[1]) then if redis.call('get',KEYS[1])==ARGV[1] then return redis.call('expire',KEYS[1],ARGV[2]) else return 0 end end";
 
      RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
 
      Object result = redisTemplate.execute(redisScript,new StringRedisSerializer(),new StringRedisSerializer(), Collections.singletonList(lockKey),value,expireTime + "");
      System.out.println(result + "-----------");
      //Object result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey),value,expireTime + "");
 
      if(SUCCESS.equals(result)){
        return true;
      }
 
    }catch(Exception e){
      e.printStackTrace();
    }
    return ret;
  }
 
  /**
   * 釋放鎖
   * @param lockKey
   * @param value
   * @return
   */
  public boolean releaseLock(String lockKey, String value){
 
    String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
 
    RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
 
    Object result = redisTemplate.execute(redisScript,new StringRedisSerializer(),new StringRedisSerializer(), Collections.singletonList(lockKey),value);
    if(SUCCESS.equals(result)) {
      return true;
    }
 
    return false;
  }

   以上代碼已經在我的項目中確切可以使用了。但是在使用的過程中遇到了許多問題。

   1:java.lang.IllegalStateException

   在返回值方面,會經常報IllegalStateException。

RedisScript<String> redisScript = new DefaultRedisScript<>(script, String.class);

   用String類型時候,經常會報類型轉換異常。我在代碼中使用的Long類型接收該類型,在命令行中我們也看到命令行結果返回的是數字0或者1,保險起見我們也可以用Object對象來接收結果集。

 2:ERR value is not an integer or out of range

  這個問題糾結了我一個下午至少,Redis報的異常都是很深的,從跟蹤源碼的時候看到,我們在調用redisTemplate.execute的方法時候,如果不傳序列化的參數的時候,代碼默認調用的是 Jdkserializationredisserializer 來進行序列化和反序列化操作,這是jdk自帶的序列化操作,使用該序列化的對象必須要實現Serializable接口。所以該序列化接口是用于對實體類的序列化。

   所以在進行 execute 操作的時候,我們傳入 Stringredisserializer,該序列化接口是專用于對字符串類型的序列化操作。具體的區別可以去這兩個類的源碼中看下他們的加密方式。 

因為時間以及個人能力的問題,對部分源碼有點未理解,所以沒有做到全方位的解讀這些異常的原因,以后有機會會將源碼細讀并分析其異常原因。

感謝你能夠認真閱讀完這篇文章,希望小編分享的“RedisTemplate下Redis分布式鎖引發的系列問題有哪些”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,更多相關知識等著你來學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

牙克石市| 子洲县| 喀喇沁旗| 亳州市| 奉贤区| 霍林郭勒市| 东山县| 平遥县| 雷波县| 灌阳县| 关岭| 北碚区| 绍兴市| 清河县| 嘉黎县| 嘉荫县| 湖南省| 合阳县| 岚皋县| 连山| 阳曲县| 克东县| 通化县| 三门县| 朔州市| 武安市| 昆明市| 镇平县| 象州县| 敖汉旗| 余庆县| 祁门县| 紫阳县| 望奎县| 新兴县| 宁河县| 万年县| 新和县| 武川县| 卫辉市| 汤阴县|