Redisson
Redisson是一个在Redis的基础上实现的java驻内存数据网格,就是提供了一系列的分布式的java对象
官方地址:https://redisson.org
Redisson分布式锁
第一步:引入依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.13.6</version>
</dependency>
第二步:配置Redisson客户端:在配置文件中进行配置
这里通过配置类,完成对Redis的基本配置,在调用Redisson的创建方法根据这个配置类,创建出对应的客户端操作对象RedissonClient
@Configuration
public class RedisConfig {
public RedissonClient redissonClient(){
//配置类
Config config=new Config();
//添加redis地址,这里添加单点的地址,也可以使用config.useClusterServers()添加集群地址
config.useSingleServer().setAddress("redis://192.168.75.135.101:6379").setPassword("520521");
//创建客户端
return Redisson.create(config);
}
}
如何使用Redisson提供的分布式锁
@Resource
private RedissonClient redissonClient;
@Test
void testRedisson() throws InterruptedException {
//获取锁(可重入),指定锁的名称
RLock nihao = redissonClient.getLock("nihao");
//尝试获取锁,参数分别是,最大等待时间,锁自动释放时间,时间单位
boolean b = nihao.tryLock(1, 10, TimeUnit.SECONDS);
//判断锁释放获取成功
if(b){
try{
System.out.println("执行业务");
}finally {
//释放锁
nihao.unlock();
}
}
}
Redisson可重入锁
原理就是在存放value的时候通过计数器机制进行重复访问,当同一个线程在此访问这个锁的时候会进行判断,是不是自己同一个线程,是的话计数器加1,同理释放锁的时候就是将该计数器的数字减1
基本流程:
要实现上述操作功能需要用到Lua脚本
local key = KEYS[1];-- 锁的key
local threadId = ARGV[1];-- 线程唯一标识
local releaseTime=ARGV[2];--锁的自动释放时间
-- 判断当前锁是否还是被自己持有
if(redis.call('HEXISTs",key,threadId)==0)then
return nil;--如果已经不是自己,则直接返回
end;
--是自己的锁,则重入次数-
1local count =redis.call('HINCRBY",key,threadId,-1);
--判断是否重入次数是否已经为0
if(count>0)then
--大于0说明不能释放锁,重置有效期然后返回
redis.call('EXPIRE",key, releaseTime);
return nil;
--等于0说明可以释放锁,直接删除
else
redis.call('DEL',key);
return nil;
end ;
这是可复用锁的底层原理
在Redisson的操作类中已经实现了可重复锁的相关实现,在获取锁的同时会自动进行重复锁判断
Redission解决超时释放的问题
Redisson底层通过使用Redis的watch机制和Lua脚本来实现解决超时释放锁的问题。当一个线程在获取锁时,Redisson会将锁的持有者信息保存到Redis中,并启动一个定时任务来监控锁的有效期。当锁的有效期超时时,定时任务会使用Lua脚本来检查锁是否还是当前线程持有,如果是则延长锁的有效期,如果不是则释放锁。这种机制可以确保在锁超时释放的情况下,不会出现并发访问问题。通过这种方式,Redisson能够在底层实现有效地解决超时释放的问题。
Redission解决锁的判断一次性问题
前面讲到在进行判断的时候如果没有拿到锁就会返回失败,Redis中提供了重试机制,设置一个最大等待时间,期间进行重复获取锁的操作
Redission分布式锁主从一致性问题
在当下的场景中都是多台Redis服务器进行主从服务器,假定这时有一个线程访问中Redis服务器,成功获取锁,就在这时,主服务器与从服务器同步数据的时候主服务器出现故障,这时Redis哨兵会从从服务器中选出一个来当主服务器,这是锁信息并未同步,就在这时其他线程也能够得到锁进行操作,就会出现问题
解决方案:
在Redisson中提供联锁方案,就是需要同时给多个Redis节点所送锁的信息,这时后就要判断,如果有一个失败,那么所有的节点获取锁都会失败
Rlock lock1=redissonClient.getLock("order");
Rlock lock2=redissonClient2.getLock("order");
Rlock lock3=redissonClient3.getLock("order");
//创建联锁
redissonClient.getMultiLock(lock1,lock2,lock3);