1.如何实现分布式锁
Redis天生就可以作为一个分布式系统来使用,所以它实现的锁都是分布式锁。
Redis可以通过setnx(setifnotexists)命令实现分布式锁~
setnxmylocktrue-加锁
delmylock-释放锁
通过执行结果是否为1可以判断是否成功获取到锁~
2.Redis分布式锁存在什么问题
Redis分布式锁存在两个问题:
死锁问题:未设置过期时间,锁忘记释放,加锁后还没来的及释放锁就宕机了都会导致死锁问题.
锁误删问题:设置了超时时间,但是线程执行超过超时时间后锁误删问题.
2.1解决死锁问题
MySQL中解决死锁问题是通过设置超时时间,Redis也是如此,但是问题来了,第一步先加锁,然后再设置超时时间,那么就不满足原子性了,那么怎么办?
官方在Redis2.6.12版本之后,新增了一个功能,我们可以使用一条命令既执行加锁操作,又设置超时时间:setnx和expire
第一条命令成功加锁,并设置30s过期时间
第二条命令跟在第一条命令后,还没有超过30s,所以获取失败
2.2解决锁误删问题
锁误删问题是解决死锁问题带来的问题,如何理解?
既然知道了什么是锁误删问题,那么如何解决?
答:可以通过添加锁标识来解决.
前面我们使用set命令的时候,只使用到了key,那么可以给value设置一个标识,表示当前锁归属于那个线程,例如value=thread1,value=thread2.....
但是这样解决依然存在问题,因为新增锁标识之后,线程在释放锁的时候,需要执行两步操作了:
判断锁是否属于自己
如果是,就删除锁
这样就不能保证原子性了,那该怎么办?
解决方案
使用lua脚本来解决(Redis本身就能保证lua脚本里面所有命令都是原子性操作)
使用Redisson框架来解决(主流)
那么Redisson如何实现分布式锁呢?
代码示例
1.引入Redisson依赖
dependency
groupIdorg.redisson/groupId
artifactIdredisson-spring-boot-starter/artifactId
version3.23.2/version
/dependency
2.创建RedissonClient对象
ConfigurationpublicclassRedissonConfig{
BeanpublicRedissonClientredissonClient(){
Configconfig=newConfig();
config.useSingleServer().setAddress("redis://.0.0.1:");
//如果有密码需要设置密码
returnRedisson.create(config);
}
}
3.调用分布式锁
RestControllerpublicclassLockController{
ResourceprivateRedissonClientredissonClient;
RequestMapping("/lock")publicStringlockResource()throwsInterruptedException{
StringlockKey="myLock";
//获取锁
RLocklock=redissonClient.getLock(lockKey);
try{
//超时时间10s,[tryLock获取成功才需要释放锁,获取失败不需要释放锁]
booleanisLocked=lock.tryLock(20,TimeUnit.SECONDS);
if(isLocked){
//成功获取到锁
try{
TimeUnit.SECONDS.sleep(5);
return"成功获取到锁,并执行业务代码";
}catch(InterruptedExceptione){
e.printStackTrace();
}finally{
//释放锁
lock.unlock();
}
}else{
//获取锁失败
return"获取锁失败";
}
}catch(InterruptedExceptione){
e.printStackTrace();
}
return"获取锁成功";
}
}
启动项目,使用端口访问接口: