通过redis实现分布式锁
- 1.什么是分布式锁?
- 2.分布式锁如何实现?
- 注意点:
- 3.分布式锁使用中常见问题?
- 问题二:业务执行时间超过锁的过期时间?
- 1. 解决方案:
- 2.操作步骤:
- 问题二:一个客户端释放了其他客户端持有的锁?
- 1.产生原因:
- 2.解决办法:
1.什么是分布式锁?
提到分布式锁就要提到单机锁。
单机锁:多线程程序中,为了避免同时操作同一个共享变量产生的数据问题(脏读、幻读、不可重复读等),通常需要一把锁来-互斥,以保证共享变量的正确性,其使用范围是在同一个进程中。
分布式锁:项目开发过程中,如果你的项目是部署到多台服务器上的微服务架构,意味着一个服务会部署多个进程,此时为了避免多个进程操作同一个变量导致的数据错误,需要引入[分布式锁来解决这个问题了。
真实项目举例
我之前写的一个项目是通过定时任务去解析压缩包,将压缩包里面的txt文本解析成java对象,保存到数据库中。(成功保存后,修改压缩包状态为已解析,定时任务不会去重复执行)定时任务设置的执行周期是30秒,假设txt文本过大,解析时间为60秒这样的话就会存在重复解析的问题。而我的项目又是一个分布式系统,最终决定通过redis实现分布式领来解决重复解析的问题。
2.分布式锁如何实现?
想要实现分布式锁,就需要Redis具备互斥能力,我是通过redisson来实现分布式锁的。解析代码如下:
// 获取分布式锁对象
RLock rlock = redissonClient. getlock (key);
// 为当前进程执行加锁操作,返回true表示加锁成功
Boolean islock=rlock.trylock();
此时进程2也申请获取锁执行以上方法发现锁存在,则返回false,表示未获取镜,不执行解析方法
// 释放锁
public void releaselock () {
rLock.unLock ();
}
注意点:
1.获取锁执行完业务逻辑之后必须释放锁给其他进程执行业务逻辑的机会。
2.获取锁的同时必须设置过期时间,否则当前进程可能会一直占用这把锁,其他进程永远拿不到这把锁了,造成死锁。
以下场景就会造成死锁:
1.业务逻辑出现异常,无法释放锁。
2.当前进程挂了,无法释放锁。
3.分布式锁使用中常见问题?
场景分析:
1.客户端1获取锁成功,开始执行业务逻辑。
2.客户端1执行业务逻辑时间,超过锁的过期时间.锁被自动释放.
3.客户端2获取锁成功,开始执行业务逻辑。
4.客户端1执行完业务逻辑,释放锁一但释放的是客户端2的锁。
问题二:业务执行时间超过锁的过期时间?
1. 解决方案:
加锁时,先设置一个过期时间,然后开启一个守护线程,定时去检测这个锁的失效时间,如果锁快过期了,而业务还没执行完,此时自动对锁进行续期。
一般通过Redisson来实现:看网上的文章,这个守护线程被称作看门狗。
2.操作步骤:
1.引入redisson依赖
< dependency >
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.9.1</version>
</dependency>
2.编写配置类,初始化一个RedissonClient对象
// redisson 配置类
@author 三十六烦恼风
@since 2021/08/10
@Configuration
public class MyRedissonConfig {
// @Bean
publicRedissonClient redisson() throws IOException {
// 1.创建配置.
Config config = new Config();
//配置redis节点模式
config. usersingleserver ().setAddress ("redis:// 127. 0. e.1:6379");
return Redisson.create(config);.
)
3.注入该对象
@Autowired
Private RedissonClient redisson;
4.获取RLock对象
RLock rlock = redisson.getLock(key);
5.设置过期时间
isLock=rLock.tryLock();
只有不传参数的情况下才会默认对当前锁进行自动续期操作,传参则表明对当前镇设置了过期时间,到期自动解锁,不会进行自动续期。
使用看门狗之后,会自动对锁进行续期。默认30秒,当业务逻辑未执行完时,会对锁续期30秒,直到业务逻辑执行完,30秒之后,自动释放锁
6.rlock.unLock()释放锁.
问题二:一个客户端释放了其他客户端持有的锁?
1.产生原因:
每个客户端释放锁的时候,都是无脑操作的,并不会判断当前锁是否还属于自己,从而导致释放别人的锁的风险。
2.解决办法:
1.加锁时设置一个唯一标识key在释放锁时先判断key是否属于自己如果属于才释放
if(redis.get("lock")=$uuid) {
Redis.del("lock");
}
注意:判断和删除是两步操作,为了程序安全,应保证其原子性。
Lua脚本一交给redis来执行
因为redis处理每一个请求是单线程执行的,在执行一个Lua脚本时,其他请求必须等待。直到这个脚本执行完成。
这样可以保证其原子性
我个人认为只要问题一解决了,问题二也就不存在了。