Redisson分布式锁的原理简介
在解决并发安全问题的时候,思路其实就是将并发执行控制为串行执行,这就是锁的具体表现。
在传统的单机模式下,synchronized关键字、ReentrantLock、CAS等方案的单机锁是可行的,但是分布式架构的微服务,一个服务多个节点的场景就需要Redisson等分布式锁来处理。
经典的秒杀场景下,订单服务的某个接口被大量并发请求访问,如何控制商品超卖问题?
通过Redisson锁住商品的sku或主键ID,至此同一时间只能有一个服务节点的一个线程来执行该接口的后续代码,执行完毕后释放锁,就能实现需求了。
这是Redisson分布式锁的常规用法,其底层实现是怎样的呢?
-
基于Redis命令的实现: Redisson利用了Redis的单线程特性和原子操作的特点。它通过调用Redis的SETNX(SET if Not Exists)命令来尝试获取锁,当key不存在时,才能获取到锁。同时,Redisson还可以设置锁的自动过期时间,以防止因为某些原因导致锁一直被持有而无法释放。
-
心跳续约(看门狗)机制: (未设置锁的过期时间)为了防止锁提前释放,看门狗线程会定期检查锁的状态,若锁仍未被释放且即将过期,则会向Redis服务器发送续约请求,延长锁的过期时间。。
-
实现可重入锁: Redisson支持可重入锁,保证同一线程在持有锁的情况下能够多次获取锁,而不会因为自己已经持有锁而被阻塞。
-
分布式锁释放的安全性保证: Redisson通过Lua脚本来释放锁,保证了释放锁的原子性。使用Lua脚本可以保证释放锁的操作是原子的,避免了在执行释放锁逻辑时出现的并发问题。
如何在项目中配置Redisson Client?
@Configuration
public class IocBean {
@Value("${spring.redis.host}")
private String redisHost;
@Value("${spring.redis.username}")
private String redisUsername;
@Value("${spring.redis.password}")
private String redisPassword;
@Value("${spring.redis.port}")
private String redisPort;
@Bean
public RedissonClient redissonClient() {
Config config = new Config();
// 构建Redis连接字符串,非SSL连接
String address = "redis://" + redisHost + ":" + redisPort;
// SSL连接
// String address = "rediss://" + redisHost + ":" + redisPort;
config.useSingleServer()
.setAddress(address)
.setUsername(redisUsername)
.setPassword(redisPassword);
return Redisson.create(config);
}
}
为什么需要可重入锁?
假设A函数内部在调用其他服务的B函数(也有锁),若无法支持可重入锁,则会造成死锁。为了支持可重入锁,通常采用线程ID或唯一业务ID用作Redis的key以支持获取锁的能力。
看门狗机制如何实现自动续约?
为了保证请求的行为执行完毕,有一个后台定时任务自动续约(延长key的过期时间),当行为执行完会调用unlock删除key,确保行为执行完成再释放锁。
如何避免死锁?
- 判断线程是否存活,若线程已经处于非常状态,则释放锁。(无法规避线程阻塞的情况)
- 设置业务过期时间,行为若在业务过期时间内未完成,主动删除key释放锁。

浙公网安备 33010602011771号