Redis与Redisson的分布式锁

基于 Redis 的分布式锁

其实 Redis 官网已经给出了实现:https://redis.io/topics/distlock,说各种书籍和博客用了各种手段去用 Redis 实现分布式锁,建议用 Redlock 实现,这样更规范、更安全

我们默认指定大家用的是 Redis 2.6.12 及更高的版本,就不再去讲 setnx、expire 这种了,直接 set 命令加锁

set key value[expiration EX seconds|PX milliseconds] [NX|XX]

# 例如
SET resource_name my_random_value NX PX 30000

SET 命令的行为可以通过一系列参数来修改

  • EX second:设置键的过期时间为 second 秒。SET key value EX second 效果等同于 SETEX key second value
  • PX millisecond:设置键的过期时间为 millisecond 毫秒。SET key value PX millisecond 效果等同于 PSETEX key millisecond value
  • NX:只在键不存在时,才对键进行设置操作。SET key value NX 效果等同于 SETNX key value
  • XX :只在键已经存在时,才对键进行设置操作

这条指令的意思:当 key——resource_name 不存在时创建这样的 key,设值为 my_random_value,并设置过期时间 30000 毫秒。因为 Redis 是单线程的,这一条指令不会被打断,所以是原子性的操作

Redis 实现分布式锁的主要步骤

  • 指定一个 key 作为锁标记,存入 Redis 中,指定一个唯一的标识作为 value
  • key 不存在时才能设置值,确保同一时间只有一个客户端进程获得锁,满足 互斥性 特性
  • 设置一个过期时间,防止因系统异常导致没能删除这个 key,满足 防死锁 特性
  • 当处理完业务之后需要清除这个 key 来释放锁,清除 key 时需要校验 value 值,需要满足 解铃还须系铃人

设置一个随机值的意思是在解锁时候判断 key 的值和我们存储的随机数是不是一样,一样的话,才是自己的锁,直接 del 解锁就行

当然这个两个操作要保证原子性,所以 Redis 给出了一段 lua 脚本(Redis 服务器会单线程原子性执行 lua 脚本,保证 lua 脚本在处理的过程中不会被任意其它请求打断)

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

Redis 实现分布式锁的问题

  • 获取锁时,过期时间要设置多少合适呢?预估一个合适的时间,其实没那么容易,比如操作资源的时间最慢可能要 10s,而我们只设置了 5s 就过期,那就存在锁提前过期的风险。这个问题先记下,我们先看下 Javaer 要怎么在代码中用 Redis
  • 容错性如何保证呢?Redis 挂了怎么办,你可能会说上主从、上集群,但也会出现这样的极端情况,当我们上锁后,主节点就挂了,这个时候还没来的急同步到从节点,主从切换后锁还是丢了

带着这两个问题,我们接着看

基于 Redisson 的分布式锁

Redisson 概述

redissonRedis 官方的分布式锁组件。GitHub 地址:https://github.com/redisson/redisson

Redisson 是一个在 Redis 的基础上实现的 Java 驻内存数据网格。它不仅提供了一系列的分布式的 Java 常用对象,还实现了可重入锁 ReentrantLock、公平锁 FairLock、联锁 MultiLock、 红锁 RedLock、 读写锁 ReadWriteLock 等,还提供了许多分布式服务。Redisson 提供了使用 Redis 的最简单和最便捷的方法。Redisson 的宗旨是促进使用者对 Redis 的关注分离,从而让使用者能够将精力更集中地放在处理业务逻辑上

Redisson 支持单点模式、主从模式、哨兵模式、集群模式,只是配置的不同

Redisson 实现分布式锁

Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379").setPassword("").setDatabase(1);
RedissonClient redissonClient = Redisson.create(config);
RLock disLock = redissonClient.getLock("mylock");
boolean isLock;

try {
   
	/*尝试获取锁的最大等待时间是100秒,超过这个值还没获取到,就认为获取失败锁的持有时是10秒*/
	isLock = disLock.tryLock(100, 10, TimeUnit.MILLISECONDS);
  	if (isLock) {
   
    	// 做自己的业务
    	Thread.sleep(10000);
  	}
} catch (Exception e) {
   
	e.printStackTrace();
} finally {
   
	disLock.unlock();
}

Redisson 分布式锁的源码

先看下 RLock 的类关系

在这里插入图片描述
跟着源码,可以发现 RedissonLockRLock 的直接实现,也是我们加锁、解锁操作的核心类

RLock 接口

// 获取RLock对象
RLock lock = redisson.getLock("myLock");

RLock 提供了各种锁方法,我们来解读下这个接口方法。可以看到继承自 JDKLock 接口和 Reddsion 的异步锁接口 RLockAsync

public interface RLock extends Lock, RLockAsync {
   

    /*获取锁的名字*/
    String getName();
    
    /**
     * 这个叫终端锁操作,表示该锁可以被中断 假如A和B同时调这个方法,A获取锁,B为获取锁,那么B线程可以通过
     * Thread.currentThread().interrupt(); 方法真正中断该线程
     */
    void lockInterruptibly(long leaseTime, TimeUnit unit) throws InterruptedException
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值