为什么引入分布式锁?
在微服务中, 例如一个订单系统需要调用不同的商品库, 商品库里的数据保持一致,那么在不同的微服务之间, 一次只能由一个请求来访问, 那么在对两个库上就要加一个东西来识别,有没有线程在访问这个共同的资源,这即使分布式锁的思想
●特性
实现分布式锁
●方式 1:SETNX 命令
要实现分布式锁,必须要求 Redis 有互斥的能力。可以使用 SETNX 命令,
其含义是 SET IF NOT EXIST,即如果 key 不存在,才会设置它的值,否则什么
也不做。两个客户端进程可以执行这个命令,达到互斥,就可以实现一个分布式
锁
代码
/*
扣库存方法
分布式锁1:
*/
@RequestMapping(path = "/sub")
public String subStock(){
//尝试向redis中设置一个键(锁),哪个线程设置成功,就获得锁, 设置成功返回true,失败则返回false
String clientid = UUID.randomUUID().toString();
try {
boolean res = redisTemplate.opsForValue().setIfAbsent("stock_lock",clientid,15, TimeUnit.SECONDS);
if(!res){
//获取锁失败
return "fail";
}
//查询库存
Integer count = (Integer) redisTemplate.opsForValue().get("stock");
if(count>0){
count = count-1;
redisTemplate.opsForValue().set("stock",count);
return "success";
}else{
return "fail";
}
}finally {
String id = (String)redisTemplate.opsForValue().get("stock_lock");
if(id.equals(clientid)){
redisTemplate.delete("stock_lock");// 确保键最终是会被删除释放的
}
}
}
这里由两个要点:
- 要给该代码添加finally,保证最后会释放锁,防止死锁; 例如程序报错,宕机
- 给redis的key添加失效时间, 防止该线程出错,或者该服务宕机之后, 造成持续持有锁__死锁, 使得其他线程无法操作
- 添加UUID,在finally要释放锁之前要判断, 保证是释放当前的锁
但是设置失效时间 10s,有可能业务执行时间大于 10s,那么锁会失效,导致其
他线程进入到减库存业务中,这时,第一个线程执行完成,会误删除第二个线程的
锁标志,导致其他线程进入.导致锁永久失效.
所以锁的失效时间一直是一个问题
所以就有了下面的方案
方式 2: 使用 redission
●依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.6.5</version>
</dependency>
●创建 Redisson 对象
//创建 Redisson 对象
@Bean
public Redisson getRedisson() {
Config config = new Config();
config.useSingleServer().setAddress("redis://120.48.37.232:6379").se
tDatabase(0);
return (Redisson) Redisson.create(config);
}
●使用 Redisson 实现加锁,释放锁.
此时就算程序运行时间超过了设置的时间,RLock锁也会添加时间,防止其他线程进入;
始终保证该程序运行完成,同时finally会保证程序不会造成死锁