Redisson分布式锁的原理简介
在解决并发安全问题的时候,思路其实就是将并发执行
控制为串行执行
,这就是锁的具体表现。
在传统的单机模式下,synchronized关键字、ReentrantLock、CAS等方案的单机锁是可行的,但是分布式架构的微服务,一个服务多个节点的场景就需要Redisson等分布式锁来处理。
经典的秒杀场景下,订单服务的某个接口被大量并发请求访问,如何控制商品超卖问题?
通过Redisson锁住商品的sku或主键ID,至此同一时间只能有一个服务节点的一个线程来执行该接口的后续代码,执行完毕后释放锁,就能实现需求了。
这是Redisson分布式锁的常规用法,其底层实现是怎样的呢?
-
基于Redis命令的实现: Redisson利用了Redis的单线程特性和原子操作的特点。它通过调用Redis的SETNX(SET if Not Exists)命令来尝试获取锁,当key不存在时,才能获取到锁。同时,Redisson还可以设置锁的自动过期时间,以防止因为某些原因导致锁一直被持有而无法释放。
-
心跳续约(看门狗)机制: 为了防止因为持有锁的进程意外宕机而导致锁无法释放,Redisson在获取锁之后会启动一个定时任务来周期性地续约锁的有效时间。这样即使持有锁的进程意外宕机,锁也会在一定时间后自动释放(避免死锁)。
-
实现可重入锁: 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);
}
}