Redis

关注公众号 jb51net

关闭
首页 > 数据库 > Redis > Redis分布式锁

Redis分布式锁的几种实现方法

作者:今天多喝热水

本文主要介绍了Redis分布式锁的几种实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

Redis基本命令:

// 设置键myKey的值为myValue,并且该键在10秒后过期
SET myKey myValue EX 10
// 设置键myKey的值为myValue,并且该键在1000毫秒(1秒)后过期
SET myKey myValue PX 1000
// 指定key过期时间,单位是秒,过期后自动删除
EXPIRE key_name second_num
// 指定key过期时间,单位是毫秒,过期后自动删除
PEXPIRE key_name millisecond_num

// 返回key过期时间
TTL key_name 

SET key value	 //设置键key的值为value
SETNX key value	 //只有在键key不存在的情况下,将key的值设置为value
SETEX key seconds value	 //将键key的值设置为value,并且超时时间为seconds秒
PSETEX key milliseconds value  //将键key的值设置为value,并且超时时间为milliseconds毫秒

一、基础方案:SETNX命令实现

public class SimpleRedisLock {
    private Jedis jedis;
    private String lockKey;

    public SimpleRedisLock(Jedis jedis, String lockKey) {
        this.jedis = jedis;
        this.lockKey = lockKey;
    }
    public boolean tryLock() {
        Long result = jedis.setnx(lockKey, "locked");
        if (result == 1) {
            jedis.expire(lockKey, 30); // 设置过期时间
            return true;
        }
        return false;
    }
    public void unlock() {
        jedis.del(lockKey);
    }
}

// 使用示例
Jedis jedis = new Jedis("localhost");
SimpleRedisLock lock = new SimpleRedisLock(jedis, "order_lock");
try{
    if(lock.tryLock()){
    // 业务逻辑
    }
} finally {
    lock.unlock();
}

问题分析:

二、改进方案:原子SET命令

public class AtomicRedisLock {
    private Jedis jedis;
    private String lockKey;
    private String clientId;

    public SimpleRedisLock(Jedis jedis, String lockKey) {
        this.jedis = jedis;
        this.lockKey = lockKey;
        this.clientId = UUID.randomUUID().toString();
    }
    public boolean tryLock(int expireSeconds) {
        String result = jedis.set(lockKey, clientId, SetParams.setParams().nx().ex(expireSeconds));
        return "OK".equals(result);
    }
    public boolean unlock() {
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                "return redis.call('del', KEYS[1]) " +
                "else return 0 end";
        Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(clientId));
        return result.equals(1L);
    }
}

// 使用示例
Jedis jedis = new Jedis("localhost");
AtomicRedisLock lock = new AtomicRedisLock(jedis, "payment_lock");
try{
    if(lock.tryLock(30)){
    // 业务逻辑
    }
} finally {
    lock.unlock();
}

核心改进:

仍然存在的问题:

三、高可用方案:RedLock算法

public class RedLock {
    pprivate List<Jedis> jedisList;
    private String lockKey;
    private String clientId;
    private int quorum;

    public RedLock(List<Jedis> jedisList, String lockKey) {
        this.jedisList = jedisList;
        this.lockKey = lockKey;
        this.clientId = UUID.randomUUID().toString();
        this.quorum = jedisList.size() / 2 + 1;
    }
    public boolean tryLock(int expireMillis) {
        long startTime = System.currentTimeMillis();
        // 第一阶段:尝试获取多数节点锁
        int successCount = 0;
        for (Jedis jedis : jedisList) {
            if (tryAcquire(jedis, expireMillis)) {
                successCount++;
            }
            if ((System.currentTimeMillis() - startTime) > expireMillis) {
                break;
            }
        }
        // 第二阶段:验证锁有效性
        if (successCount >= quorum) {
            long validityTime = expireMillis - (System.currentTimeMillis() - startTime);
            return validityTime > 0;
        }
        // 第三阶段:释放已获得的锁
        for (Jedis jedis : jedisList) {
            release(jedis);
        }
        return false;
    }
    private boolean tryAcquire(Jedis jedis, long expireMillis) {
        try {
            String result = jedis.set(lockKey, clientId, SetParams.setParams().nx().px(expireMillis));
            return "OK".equals(result);
        } catch (Exception e) {
            return false;
        }
    }
    private void release(Jedis jedis) {
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                "return redis.call('del', KEYS[1]) else return 0 end";
        jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(clientId));
    }
}

部署要求:

适用场景:

四、生产级方案:Redisson实现

// 配置Redisson客户端
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient redisson = Redisson.create(config);
// 获取锁对象
RLock lock = redisson.getLock("orderLock");
try {
    // 尝试加锁,最多等待100秒,锁定后30秒自动解锁
    boolean isLock = lock.tryLock(100, 30, TimeUnit.SECONDS);
    if (isLock) {
        // 处理业务
    }
} finally {
    lock.unlock();
}

// 关闭客户端
redisson.shutdown();
// 自动续期机制(Watchdog),Watchdog实现原理(简化版)
private void renewExpiration() {
    Timeout task = commandExecutor.schedule(() -> {
        if (redisClient.eval(...)){ // 检查是否仍持有锁
            expireAsync(); // 续期
            renewExpiration(); // 递归调用
        }
    }, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);
}

核心特性:

到此这篇关于Redis分布式锁的几种实现方法的文章就介绍到这了,更多相关Redis分布式锁内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

您可能感兴趣的文章:
阅读全文