分布式锁的特点和实现

  • Post author:
  • Post category:其他




分布式锁



什么是分布式锁

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



分布式锁具有的特点

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



分布式锁的实现

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

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



基于Redis的分布式锁

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

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

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

    public interface ILock {
    
        /**
         * 尝试获取锁
         * @param timeoutSec 锁持有的超时时间,过期后自动释放
         * @return true代表获取锁成功; false代表获取锁失败
         */
        boolean tryLock(long timeoutSec);
    
        /**
         * 释放锁
         */
        void unlock();
    
    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());
        }
    }
    
    -- 比较线程标示与锁中的标示是否一致
    if(redis.call('get', KEYS[1]) ==  ARGV[1]) then
        -- 释放锁 del key
        return redis.call('del', KEYS[1])
    end
    return 0
    



版权声明:本文为qq_43765199原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。