• 分布式锁的特点和实现


    分布式锁

    什么是分布式锁

    满足分布式系统或集群模式下多进程可见并且互斥的锁

    分布式锁具有的特点

    • 高可用
    • 多进程可见
    • 互斥
    • 高性能
    • 安全性

    分布式锁的实现

    分布式锁的核心是实现多进程之间互斥,而满足这一点的方式有很多,常见的有三种:

    MySQLRedisZookeeper
    互斥利用mysql本身的互斥锁机制利用setnx这样的互斥命令利用节点的唯一和有序性实现互斥
    高可用
    高性能一般一般
    安全性断开连接,自动释放锁利用锁超时时间,到期后自动释放锁临时节点,断开连接自动释放

    基于Redis的分布式锁

    实现分布式锁时需要实现的两个基本方法:

    • 获取锁:互斥-确保只能一个线程获取锁(setnx lock thread1)

    • 释放锁:手动释放(del lock) 超时自动释放锁(获取锁的时候添加一个超时时间)

      public interface ILock {
      
          /**
           * 尝试获取锁
           * @param timeoutSec 锁持有的超时时间,过期后自动释放
           * @return true代表获取锁成功; false代表获取锁失败
           */
          boolean tryLock(long timeoutSec);
      
          /**
           * 释放锁
           */
          void unlock();
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      public class SimpleRedisLock implements ILock {
          private final String name;
          private StringRedisTemplate stringRedisTemplate;
      
          public SimpleRedisLock(String name, StringRedisTemplate stringRedisTemplate) {
              this.name = name;
              this.stringRedisTemplate = stringRedisTemplate;
          }
      
          private static final String KEY_PREFIX = "lock:";
          private static final String ID_PREFIX = UUID.randomUUID().toString(true) + "-";
      
          private static final DefaultRedisScript<Long> UNLOCK_SCRIPT;
          static {
              UNLOCK_SCRIPT = new DefaultRedisScript<>();
              //执行的lua文件
              UNLOCK_SCRIPT.setLocation(new ClassPathResource("unlock.lua"));
              //脚本方法执行完成后的返回值
              UNLOCK_SCRIPT.setResultType(Long.class);
          }
      
          @Override
          public boolean tryLock(long timeoutSec) {
              // 获取线程标示
              String threadId = ID_PREFIX + Thread.currentThread().getId();
              // 获取锁
              Boolean success = stringRedisTemplate.opsForValue()
                      .setIfAbsent(KEY_PREFIX + name, threadId, timeoutSec, TimeUnit.SECONDS);
              //自动拆箱:注意空指针的可能性,这里当success为空的时候,返回false
              return Boolean.TRUE.equals(success);
          }
      
          @Override
          public void unlock() {
              // 调用lua脚本
              stringRedisTemplate.execute(
                      UNLOCK_SCRIPT,
                      Collections.singletonList(KEY_PREFIX + name),
                      ID_PREFIX + Thread.currentThread().getId());
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      -- 比较线程标示与锁中的标示是否一致
      if(redis.call('get', KEYS[1]) ==  ARGV[1]) then
          -- 释放锁 del key
          return redis.call('del', KEYS[1])
      end
      return 0
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
  • 相关阅读:
    数学建模之预测和评价类
    我们对 .NET 9 的愿景
    短信平台不知道怎么选?来看看这几个平台:
    0基础学习VR全景平台篇第142篇:VR直播下载流程
    ​Python实战案例:航班票价预测这样做,效果真好啊
    数据结构—链表
    【算法每日一练]-图论(保姆级教程 篇2(topo排序,并查集,逆元))#topo排序 #最大食物链 #游走 #村村通
    闲聊servlet的常见注册方式
    vuex刷新页面丢失登录的token信息的解决方案
    Numpy+Pandas+Matplotlib学习
  • 原文地址:https://blog.csdn.net/qq_43765199/article/details/127837446