• Redis分布式锁的实现、优化与Redlock算法探讨


    Redis分布式锁最简单的实现

    要实现分布式锁,首先需要Redis具备“互斥”能力,这可以通过SETNX命令实现。SETNX表示SET if Not Exists,即如果key不存在,才会设置它的值,否则什么也不做。利用这一点,不同客户端就能实现互斥,从而实现一个分布式锁。

    举例:

    • 客户端1申请加锁,加锁成功:
    • 客户端2申请加锁,由于它后到达,所以加锁失败:

    加锁成功

    当加锁成功的客户端操作完共享资源后,需要及时释放锁,通常通过DEL命令删除这个key即可。

    释放锁

    避免死锁

    如果加锁的客户端出现异常或挂掉,那么可能会造成死锁现象,为避免这种情况,可以给锁设置一个过期时间:

    SETNX lock 1    // 加锁
    EXPIRE lock 10  // 10s后自动过期
    

    加锁及过期

    但是,由于SETNX和EXPIRE是两条命令,无法保证其原子性,因此有可能只执行了SETNX,未执行EXPIRE。为了解决这个问题,可以使用Redis 2.6.12之后扩展的SET命令:

    SET lock 1 EX 10 NX
    

    SET EX NX

    防止锁被其他客户端释放

    为防止锁被其他客户端释放,可以在加锁时设置一个只有自己知道的“唯一标识”:

    SET lock $uuid EX 20 NX
    

    释放锁时,先检查锁是否归自己持有:

    if redis.get("lock") == $uuid:
        redis.del("lock")
    

    由于GET和DEL不是原子操作,可以使用Lua脚本来保证原子性:

    if redis.call("GET",KEYS[1]) == ARGV[1]
    then
        return redis.call("DEL",KEYS[1])
    else
        return 0
    end
    

    Java代码实现分布式锁

    package com.msb.redis.lock;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisPool;
    import redis.clients.jedis.params.SetParams;
    
    import java.util.Arrays;
    import java.util.UUID;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    
    @Component
    public class RedisDistLock implements Lock {
       
    
        private final static int LOCK_TIME = 5*1000;
        private final static String RS_DISTLOCK_NS = "tdln:";
        private final static String RELEASE_LOCK_LUA =
                "if redis.call('get',KEYS[1])==ARGV[1] then\n" +
                        "        return redis.call('del', KEYS[1])\n" +
                        "    else return 0 end";
        private ThreadLocal<String> lockerId = new ThreadLocal<>();
        private Thread ownerThread;
        private String lockName = "lock";
    
        @Autowired
        private JedisPool jedisPool;
    
        public String getLockName() {
       
            return lockName;
        }
    
        public void setLockName(String l
  • 相关阅读:
    封装了一个简单的C++ HDF5工具库,实现常用数据类型的读写
    三、Thread 类和Runnable 接口详解
    AtCoder Beginner Contest 269 G(DP)
    vcpkgC++开源项目1
    Python150题day12
    GAN基础知识及代码
    电话机器人与电销系统完美融合,AXB回拨助力外呼升级
    vue3中父组件与子组件的通信传值
    权限框架SpringSecurity(一)——自定义登录
    Linux操作系统之文件系统详解
  • 原文地址:https://blog.csdn.net/qq_26893655/article/details/139643483