问题引入
面对高并发时,mysql事务无法保证原子性,可以使用synchronized关键字,在调用方法时加锁;但是这个方式在锁住之后其他线程会阻塞,这会又很大的性能问题,于是考虑到分布式锁来解决这一问题
分布式锁
控制分布式系统有序的去对共享资源进行操作,通过互斥来保持一致性(synchronized关键字只在当前Java系统中作用);常用的分布式锁解决方案有三种:
- 基于数据库层面实现分布式锁(悲观锁,乐观锁)
- 基于缓存层面(如redis)实现分布式锁
- 基于zookeeper实现分布式锁
redis分布式锁解决方案
- 设置一个公共锁:
setnx lock-key value
,利用setnx命令的返回值特征,有值则返返回设置失败,对该数据不具有控制权;无值则返回设置成功,可以进行下一步操作,每一个请求来操作都要先拿锁,拿不到锁就不能对数据操作,锁需要设置有效时长,以免死锁,但锁的有效期可能低于程序的运行时间,所以需要看门狗对时间进行延续,redisson就帮助我们完成了这一工作,看门狗默认延期为30s超出默认时间看门狗也不会再延时 - 释放公共锁:
del lock-key
Redisson实现(集群环境)
- 导入依赖redisson-springboot-redisson-starter
- 配置文件:
redis:
cluster:
nodes: 192.168.206.128:6379,192.168.206.128:6380,192.168.206.128:6381
max-redirects: 3
database: 0
connect-timeout: 5000
password: curry
lettuce:
pool:
max-active: 1500
max-wait: 5000
max-idle: 500
min-idle: 100
shutdown-timeout: 1000
- 配置Bean
@Configuration
public class RedissonConfig {
@Value("${spring.redis.cluster.nodes}")
private List<String> urls;
@Value("${spring.redis.password}")
private String password;
@Bean
public RedissonClient redissonClient() throws IOException {
for (int i = 0; i < urls.size(); i++) {
urls.set(i,"redis://"+urls.get(i));
}
RedissonClient redisson;
Config config = new Config();
config.useClusterServers()
.setScanInterval(2000)
.setPassword(password)
.setNodeAddresses(urls);
redisson = Redisson.create(config);
return redisson;
}
}
- 获取与释放
/**
* 获取锁
* @param redissonClient 客户端对象
* @param key 锁的key
* @return 如果当前没有这个锁则返回true,否则返回false
*/
public static boolean getLock(RedissonClient redissonClient,String key){
RLock rLock = redissonClient.getLock(key);
return rLock.tryLock();
}
/**
* 释放锁
* @param redissonClient 客户端对象
* @param key 锁的key
*/
public static void unLock(RedissonClient redissonClient,String key){
RLock rLock = redissonClient.getLock(key);
rLock.unlock();
}