一:分布式锁演示-阶段一
1.流程图
2.代码实现
public Map<String, List<Catalog2Vo>> getCatalogJsonFromDbWithRedisLock() {
//1.抢占分布式锁,去redis占坑
Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", "111");
if (lock){
//加锁成功。。。执行业务
Map<String, List<Catalog2Vo>> dataFromDb = getDataFromDb();
//占到锁以后,然后解锁,删除锁
redisTemplate.delete("lock");
return dataFromDb;
} else {
//加锁失败。。。等待100ms,然后重试,自旋的方式
return getCatalogJsonFromDbWithRedisLock();
}
}
- 1.首先通过redisTemplate的setIfAbsent去抢占分布式锁(setIfAbsent相当于setnx)
- 2.然后判断是否抢占到,如果抢占到了,就查询数据库,然后删除锁,返回
- 3.如果没有抢占到,就等待100ms,然后重新抢占
3.问题分析
分析:如果在此过程中,查询数据库的时候出现了异常,没有执行删除锁的代码,别人想要再来占用,就没有坑位了,就会造成死锁问题。如果try-catch的话,运行程序的时候突然断电了,程序停止运行,还是会导致死锁。
二:分布式锁演进-阶段二(设置锁的过期时间)
1.流程图
2.代码分析
##
3.问题分析
分析:如果在设置过期时间之前,突然出现异常,还是会导致锁没有删除。我们要把设置过期时间和占锁优化,使其成为一个原子操作,就是如果占锁成功,并且设置完过期时间以后,我们在返回true,向下继续执行。
结论:设置过期时间,必须和加锁是同步的,原子的
三:分布式锁演进-阶段三
1.流程图
2.代码实现
public Map<String, List<Catalog2Vo>> getCatalogJsonFromDbWithRedisLock() {
//1.抢占分布式锁,去redis占坑
Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock","111",300,TimeUnit.HOURS);
if (lock){
//加锁成功。。。执行业务
//2.设置过期时间,必须和加锁是同步的,原子的
//redisTemplate.expire("locak",30,TimeUnit.MINUTES);
Map<String, List<Catalog2Vo>> dataFromDb = getDataFromDb();
//占到锁以后,然后解锁,删除锁
redisTemplate.delete("lock");
return dataFromDb;
} else {
//加锁失败。。。等待100ms,然后重试,自旋的方式
return getCatalogJsonFromDbWithRedisLock();
}
}
3.问题分析
分析:如果由于业务时间很长,锁自己过期了,我们直接删除,有可能把别人正在持有的锁删除了。所以占锁的时候,值指定为uuid,每个人匹配是自己 的锁才删除。
四:分布式锁演进-阶段四
1.流程图
2.代码实现
public Map<String, List<Catalog2Vo>> getCatalogJsonFromDbWithRedisLock() {
//1.抢占分布式锁,去redis占坑
String uuid = UUID.randomUUID().toString();
Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock",uuid,300,TimeUnit.HOURS);
if (lock){
//加锁成功。。。执行业务
//2.设置过期时间,必须和加锁是同步的,原子的
//redisTemplate.expire("locak",30,TimeUnit.MINUTES);
Map<String, List<Catalog2Vo>> dataFromDb = getDataFromDb();
//占到锁以后,然后解锁,删除锁
String lockValue = redisTemplate.opsForValue().get("lock");
if(uuid.equals(lockValue)){
redisTemplate.delete("lock");
}
return dataFromDb;
} else {
//加锁失败。。。等待100ms,然后重试,自旋的方式
return getCatalogJsonFromDbWithRedisLock();
}
}
- 获取对比值+对比成功删除 = 原子操作
3.问题分析
分析:如果正好判断是当前值,正要删除锁的时候,锁已经过期, 别人已经设置到了新的值。那么我们删除的是别人的锁,删除锁必须保证原子性。使用redis+Lua脚本完成。
五:分布式锁演进-阶段五-最终形态
1.流程图
2.代码实现
public Map<String, List<Catalog2Vo>> getCatalogJsonFromDbWithRedisLock() {
//1.抢占分布式锁,去redis占坑
String uuid = UUID.randomUUID().toString();
Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock",uuid,300,TimeUnit.SECONDS);
if (lock){
//加锁成功。。。执行业务
//2.设置过期时间,必须和加锁是同步的,原子的
//redisTemplate.expire("locak",30,TimeUnit.MINUTES);
Map<String, List<Catalog2Vo>> dataFromDb = null;
try {
dataFromDb = getDataFromDb();
} catch (Exception e) {
e.printStackTrace();
} finally {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
//删除锁
Integer lock1 = redisTemplate.execute(new DefaultRedisScript<Integer>(script, Integer.class), Arrays.asList("lock"), uuid);
}
//占到锁以后,然后解锁,删除锁
/*String lockValue = redisTemplate.opsForValue().get("lock");
if(uuid.equals(lockValue)){
redisTemplate.delete("lock");
}*/
return dataFromDb;
} else {
//加锁失败。。。等待100ms,然后重试,自旋的方式
return getCatalogJsonFromDbWithRedisLock();
}
}
3.结论
- 原子加锁——exnx
- 原子解锁——lua脚本
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/84105.html