1.介绍
在分布式系统中,不同节点可能同时尝试访问共享资源或数据。为了确保数据的一致性和避免竞态条件,需要一种机制来协调对这些资源的访问。分布式锁就是这样一种机制,它可以确保在任何给定时刻,只有一个节点可以持有锁,并且只有持有锁的节点可以执行关键代码块。
Redisson提供的分布式锁不仅具有互斥性、可重入性等基本特性,还支持自动续期、超时设置、公平锁等多种高级功能,使得分布式锁的使用更加灵活和强大。
2.分布式锁实现的关键
抢锁机制
- 怎么保证同一时间只有 1 个服务器能抢到锁?
3.注意事项
- 用完锁要释放
- 锁一定要加过期时间
- 如果方法执行时间过长,锁提前过期了?问题:
a. 连锁效应:释放掉别人的锁
b. 这样还是会存在多个方法同时执行的情况
解决方案:续期 - 释放锁的时候,有可能先判断出是自己的锁,但这时锁过期了,最后还是释放了别人的锁
- Redis 如果是集群(而不是只有一个 Redis),分布式锁的数据不同步怎么办?
4.实战
1)引入依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.30.0</version>
</dependency>
<!-- 版本仅供参考 -->
2)配置Redission
@Configuration
public class RedissonConfig {
@Bean
public RedissonClient redissonClient() {
// 1. 创建配置
Config config = new Config();
String redisAddress = "redis://你的IP地址:你的端口号";
config.useSingleServer().setAddress(redisAddress).setDatabase(你的数据库);
// 2. 创建实例
RedissonClient redisson = Redisson.create(config);
return redisson;
}
}
3)Redission实现的通用代码块
场景1:多个线程进来都需要处理,但只能一个一个处理(使用自旋机制处理)
// 只有一个线程能获取到锁
RLock lock = redissonClient.getLock("加锁名称");
try {
// 自旋:抢到锁并执行
while (true) {
if (lock.tryLock(0, -1, TimeUnit.MILLISECONDS)) {
// 业务实现
}
}
} catch (InterruptedException e) {
log.error("doCacheRecommendUser error", e);
} finally {
// 只能释放自己的锁
if (lock.isHeldByCurrentThread()) {
System.out.println("unLock: " + Thread.currentThread().getId());
lock.unlock();
}
}
场景2:多个线程进来只有一个线程处理即可,拿到锁的线程处理,没拿到锁的线程都不处理
// 只有一个线程能获取到锁
RLock lock = redissonClient.getLock("加锁名称");
try {
// 抢到锁并执行
if (lock.tryLock(0, -1, TimeUnit.MILLISECONDS)) {
// 业务实现
}
return;
} catch (InterruptedException e) {
log.error("doCacheRecommendUser error", e);
} finally {
// 只能释放自己的锁
if (lock.isHeldByCurrentThread()) {
System.out.println("unLock: " + Thread.currentThread().getId());
lock.unlock();
}
}
5.锁逾期带来的问题
多个线程进来,都需要处理,第一个线程处理的时间是大于上锁的时间,如果不续期,第一个线程还在处理,后续的线程拿到锁也进行处理,会引发线上问题,比如:数据不一致。
6.锁逾期的解决方案
- 设置合理的过期时间
根据业务场景和实际需求,为分布式锁设置合理的过期时间。过短的过期时间可能导致锁被提前释放,而过长的过期时间则可能导致资源浪费和潜在的死锁问题。
难度较大,需要合理的调研线上处理的时间
- 实现自动续期机制
在分布式锁的实现中,可以加入自动续期机制。当锁即将过期时,自动延长其过期时间,以确保在任务完成前锁不会被释放。例如,Redisson等分布式锁框架就提供了自动续期功能。
7.面试回答
提问:你使用Redission分布式锁是怎么实现的?
答:
从介绍场景、实现、可能引发的问题三个方面来回答
介绍场景:1.有一个定时任务的系统,这个时候采用了分布式来部署,定时任务需要从缓存同步到数据库中,这个时候定时任务需要加锁,只能一个服务器来处理即可(任务调度,防止重复操作)。2.在一个系统中有一个功能是会更新数据的,多个线程进来可能引发数据不一致的问题。例如:商品超卖问题(保证数据一致性)。
实现:1.在分布式的定时任务只需要一个服务器进行处理,此时则需要一个服务器获得锁即可,其他服务器没拿到锁则什么都不做。2.商品超卖问题,多个线程进来只有一个线程能拿到锁,其他线程则自旋等待,在拿到锁的那个线程操作完后,其他线程才能拿到锁,拿到锁之后还需要判断商品还有没有库存,有则购买,没有则提示用户。
可能引发的问题:1.锁过期了还没处理完(解决方案:给锁续期);2.自旋引发CPU飙高(解决方案;不用自旋去解决,采用线程唤醒的方式) 等等
以上只是个人理解,仅供参考!!!