<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          RedisTemplate 分布式鎖演變,Redission 分布式鎖實現(xiàn)!

          共 13588字,需瀏覽 28分鐘

           ·

          2021-10-01 00:09

          點擊下方“IT牧場”,選擇“設(shè)為星標(biāo)”

          來源 | blog.csdn.net/zhangkaixuan456/article/details/110679617

          • 基本原理
            • 階段一
            • 階段二
            • 階段三
            • 階段四
            • 階段五-最終形態(tài)
            • 4) Redisson

          分布式鎖的演進(jìn)

          基本原理

          我們可以同時去一個地方“占坑”,如果占到,就執(zhí)行邏輯。否則就必須等待,直到釋放鎖。“占坑”可以去redis,可以去數(shù)據(jù)庫,可以去任何大家都能訪問的地方。等待可以自旋的方式。

          階段一

          public Map<String, List<Catalog2Vo>> getCatalogJsonDbWithRedisLock() {
                  //階段一
                  Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock""111");
                  //獲取到鎖,執(zhí)行業(yè)務(wù)
                  if (lock) {
                      Map<String, List<Catalog2Vo>> categoriesDb = getCategoryMap();
                      //刪除鎖,如果在此之前報錯或宕機(jī)會造成死鎖
                      stringRedisTemplate.delete("lock");
                      return categoriesDb;
                  }else {
                      //沒獲取到鎖,等待100ms重試
                      try {
                          Thread.sleep(100);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                      return getCatalogJsonDbWithRedisLock();
                  }
              }

          public Map<String, List<Catalog2Vo>> getCategoryMap() {
                  ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
                  String catalogJson = ops.get("catalogJson");
                  if (StringUtils.isEmpty(catalogJson)) {
                      System.out.println("緩存不命中,準(zhǔn)備查詢數(shù)據(jù)庫。。。");
                      Map<String, List<Catalog2Vo>> categoriesDb= getCategoriesDb();
                      String toJSONString = JSON.toJSONString(categoriesDb);
                      ops.set("catalogJson", toJSONString);
                      return categoriesDb;
                  }
                  System.out.println("緩存命中。。。。");
                  Map<String, List<Catalog2Vo>> listMap = JSON.parseObject(catalogJson, new TypeReference<Map<String, List<Catalog2Vo>>>() {});
                  return listMap;
              }

          問題:1、setnx占好了位,業(yè)務(wù)代碼異常或者程序在頁面過程中宕機(jī)。沒有執(zhí)行刪除鎖邏輯,這就造成了死鎖

          解決:設(shè)置鎖的自動過期,即使沒有刪除,會自動刪除

          階段二

           public Map<String, List<Catalog2Vo>> getCatalogJsonDbWithRedisLock() {
                  Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock""111");
                  if (lock) {
                      //設(shè)置過期時間
                      stringRedisTemplate.expire("lock"30, TimeUnit.SECONDS);
                      Map<String, List<Catalog2Vo>> categoriesDb = getCategoryMap();
                      stringRedisTemplate.delete("lock");
                      return categoriesDb;
                  }else {
                      try {
                          Thread.sleep(100);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                      return getCatalogJsonDbWithRedisLock();
                  }
              }

          問題:1、setnx設(shè)置好,正要去設(shè)置過期時間,宕機(jī)。又死鎖了。解決:設(shè)置過期時間和占位必須是原子的。redis支持使用setnx ex命令

          階段三

          public Map<String, List<Catalog2Vo>> getCatalogJsonDbWithRedisLock() {
              //加鎖的同時設(shè)置過期時間,二者是原子性操作
              Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock""1111",5, TimeUnit.SECONDS);
              if (lock) {
                  Map<String, List<Catalog2Vo>> categoriesDb = getCategoryMap();
                  //模擬超長的業(yè)務(wù)執(zhí)行時間
                  try {
                      Thread.sleep(6000);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  stringRedisTemplate.delete("lock");
                  return categoriesDb;
              }else {
                  try {
                      Thread.sleep(100);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  return getCatalogJsonDbWithRedisLock();
              }
          }

          問題:1、刪除鎖直接刪除???如果由于業(yè)務(wù)時間很長,鎖自己過期了,我們直接刪除,有可能把別人正在持有的鎖刪除了。解決:占鎖的時候,值指定為uuid,每個人匹配是自己的鎖才刪除。

          階段四

          public Map<String, List<Catalog2Vo>> getCatalogJsonDbWithRedisLock() {
                  String uuid = UUID.randomUUID().toString();
                  ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
                //為當(dāng)前鎖設(shè)置唯一的uuid,只有當(dāng)uuid相同時才會進(jìn)行刪除鎖的操作
                  Boolean lock = ops.setIfAbsent("lock", uuid,5, TimeUnit.SECONDS);
                  if (lock) {
                      Map<String, List<Catalog2Vo>> categoriesDb = getCategoryMap();
                      String lockValue = ops.get("lock");
                      if (lockValue.equals(uuid)) {
                          try {
                              Thread.sleep(6000);
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          }
                          stringRedisTemplate.delete("lock");
                      }
                      return categoriesDb;
                  }else {
                      try {
                          Thread.sleep(100);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                      return getCatalogJsonDbWithRedisLock();
                  }
              }

          問題:1、如果正好判斷是當(dāng)前值,正要刪除鎖的時候,鎖已經(jīng)過期,別人已經(jīng)設(shè)置到了新的值。那么我們刪除的是別人的鎖解決:刪除鎖必須保證原子性。使用redis+Lua腳本完成

          階段五-最終形態(tài)

           public Map<String, List<Catalog2Vo>> getCatalogJsonDbWithRedisLock() {
                  String uuid = UUID.randomUUID().toString();
                  ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
                  Boolean lock = ops.setIfAbsent("lock", uuid,5, TimeUnit.SECONDS);
                  if (lock) {
                      Map<String, List<Catalog2Vo>> categoriesDb = getCategoryMap();
                      String lockValue = ops.get("lock");
                      String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then\n" +
                              "    return redis.call(\"del\",KEYS[1])\n" +
                              "else\n" +
                              "    return 0\n" +
                              "end";
                      stringRedisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList("lock"), lockValue);
                      return categoriesDb;
                  }else {
                      try {
                          Thread.sleep(100);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                      return getCatalogJsonDbWithRedisLock();
                  }
              }

          保證加鎖【占位+過期時間】和刪除鎖【判斷+刪除】的原子性。更難的事情,鎖的自動續(xù)期

          4) Redisson

          Redisson是一個在Redis的基礎(chǔ)上實現(xiàn)的Java駐內(nèi)存數(shù)據(jù)網(wǎng)格(In-Memory Data Grid)。它不僅提供了一系列的分布式的Java常用對象,還提供了許多分布式服務(wù)。

          其中包括(BitSetSetMultimapSortedSetMapListQueueBlockingQueueDequeBlockingDequeSemaphoreLockAtomicLongCountDownLatchPublish / SubscribeBloom filterRemote serviceSpring cacheExecutor serviceLive Object serviceScheduler service)

          Redisson提供了使用Redis的最簡單和最便捷的方法。Redisson的宗旨是促進(jìn)使用者對Redis的關(guān)注分離(Separation of Concern),從而讓使用者能夠?qū)⒕Ω械胤旁谔幚順I(yè)務(wù)邏輯上。

          更多請參考官方文檔: https://github.com/redisson/redisson/wiki

          干貨分享

          最近將個人學(xué)習(xí)筆記整理成冊,使用PDF分享。關(guān)注我,回復(fù)如下代碼,即可獲得百度盤地址,無套路領(lǐng)取!

          ?001:《Java并發(fā)與高并發(fā)解決方案》學(xué)習(xí)筆記;?002:《深入JVM內(nèi)核——原理、診斷與優(yōu)化》學(xué)習(xí)筆記;?003:《Java面試寶典》?004:《Docker開源書》?005:《Kubernetes開源書》?006:《DDD速成(領(lǐng)域驅(qū)動設(shè)計速成)》?007:全部?008:加技術(shù)群討論

          加個關(guān)注不迷路

          喜歡就點個"在看"唄^_^

          瀏覽 49
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩黄视频| 日本大色情www成人亚洲 | 夜夜躁恨恨躁爱躁 | 色五月婷婷影视 | 人人射人人爱 |