分布式锁原理和使用
自旋
public Map<String, List<Catelog2Vo>> getCatalogJsonFromDBWithRedisLock() {
Boolean b = redisTemplate.opsForValue().setIfAbsent(Lock, Lock, Duration.ofMinutes(1));
if (!b) {
int i = 10;
while (i > 0) {
Object result = redisTemplate.opsForValue().get(CATALOG_JSON);
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (result != null) {
System.out.println("命中缓存 db lock");
return (Map<String, List<Catelog2Vo>>) result;
}
i--;
}
throw new RuntimeException("系统繁忙,请重新访问");
}
//1.查出所有1级分类
List<CategoryEntity> selectList = baseMapper.selectList(null);
/**
* 将数据库的多次查询变成一次
*/
System.out.println("查询了数据库");
//2. 封装数据
List<CategoryEntity> level1Category = selectList.stream().filter(s -> s.getParentCid().equals(0L)).collect(Collectors.toList());
Map<String, List<Catelog2Vo>> map = level1Category.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {
//1.每一个的一级分类,查到1级分类的所有二级分类
List<CategoryEntity> categoryEntities = selectList.stream().filter(s -> s.getParentCid().equals(v.getCatId())).collect(Collectors.toList());
List<Catelog2Vo> catelog2VoList = categoryEntities.stream().map(c -> {
Catelog2Vo catelog2Vo = new Catelog2Vo();
catelog2Vo.setId(c.getCatId().toString());
catelog2Vo.setName(c.getName());
catelog2Vo.setCatalog1Id(v.getCatId().toString());
List<CategoryEntity> categoryEntities1 = selectList.stream().filter(s -> s.getParentCid().equals(c.getCatId())).collect(Collectors.toList());
List<Catelog2Vo.Catelog3Vo> collect = categoryEntities1.stream().map(c3 -> {
Catelog2Vo.Catelog3Vo catelog3Vo = new Catelog2Vo.Catelog3Vo();
catelog3Vo.setId(c3.getCatId().toString());
catelog3Vo.setName(c3.getName());
catelog3Vo.setCatalog2Id(c.getCatId().toString());
return catelog3Vo;
}).collect(Collectors.toList());
catelog2Vo.setCatalog3List(collect);
return catelog2Vo;
}).collect(Collectors.toList());
return catelog2VoList;
}));
if (map == null) {
/**
* 解决缓存穿透
*/
map = new HashMap<>();
}
redisTemplate.opsForValue().set(CATALOG_JSON, map, Duration.ofDays(1));
redisTemplate.delete(Lock);
return map;
}
public Map<String, List<Catelog2Vo>> getCatalogJsonFromDBWithRedisLock() {
String uuid = UUID.randomUUID().toString();
Boolean b = redisTemplate.opsForValue().setIfAbsent(Lock, uuid, Duration.ofMinutes(1));
if (!b) {
int i = 10;
while (i > 0) {
Object result = redisTemplate.opsForValue().get(CATALOG_JSON);
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (result != null) {
System.out.println("命中缓存 db lock");
return (Map<String, List<Catelog2Vo>>) result;
}
i--;
}
throw new RuntimeException("系统繁忙,请重新访问");
}
//1.查出所有1级分类
List<CategoryEntity> selectList = baseMapper.selectList(null);
/**
* 将数据库的多次查询变成一次
*/
System.out.println("查询了数据库");
//2. 封装数据
List<CategoryEntity> level1Category = selectList.stream().filter(s -> s.getParentCid().equals(0L)).collect(Collectors.toList());
Map<String, List<Catelog2Vo>> map = level1Category.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {
//1.每一个的一级分类,查到1级分类的所有二级分类
List<CategoryEntity> categoryEntities = selectList.stream().filter(s -> s.getParentCid().equals(v.getCatId())).collect(Collectors.toList());
List<Catelog2Vo> catelog2VoList = categoryEntities.stream().map(c -> {
Catelog2Vo catelog2Vo = new Catelog2Vo();
catelog2Vo.setId(c.getCatId().toString());
catelog2Vo.setName(c.getName());
catelog2Vo.setCatalog1Id(v.getCatId().toString());
List<CategoryEntity> categoryEntities1 = selectList.stream().filter(s -> s.getParentCid().equals(c.getCatId())).collect(Collectors.toList());
List<Catelog2Vo.Catelog3Vo> collect = categoryEntities1.stream().map(c3 -> {
Catelog2Vo.Catelog3Vo catelog3Vo = new Catelog2Vo.Catelog3Vo();
catelog3Vo.setId(c3.getCatId().toString());
catelog3Vo.setName(c3.getName());
catelog3Vo.setCatalog2Id(c.getCatId().toString());
return catelog3Vo;
}).collect(Collectors.toList());
catelog2Vo.setCatalog3List(collect);
return catelog2Vo;
}).collect(Collectors.toList());
return catelog2VoList;
}));
if (map == null) {
/**
* 解决缓存穿透
*/
map = new HashMap<>();
}
redisTemplate.opsForValue().set(CATALOG_JSON, map, Duration.ofDays(1));
Object o = redisTemplate.opsForValue().get(Lock);
if (o != null && o.equals(uuid)) {
redisTemplate.delete(Lock);
}
return map;
}
还是有问题
因为 在传输过程中需要耗时,这时候如果过期KEY,让其他线程进来创建KEY,然后数据返回到之前那个线程,删除KEY,又会把别人新加进来的key给删掉
获取值对比+对比成功删除=原子操作
redis+lua脚本实现
public static final String Lock = "Lock";
public Map<String, List<Catelog2Vo>> getCatalogJsonFromDBWithRedisLock() {
String uuid = UUID.randomUUID().toString();
Boolean b = redisTemplate.opsForValue().setIfAbsent(Lock, uuid, Duration.ofMinutes(5));
if (!b) {
System.out.println("获取分布式锁失败,等待重试");
int i = 10;
while (i > 0) {
Object result = redisTemplate.opsForValue().get(CATALOG_JSON);
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (result != null) {
System.out.println("命中缓存 db lock");
return (Map<String, List<Catelog2Vo>>) result;
}
i--;
}
throw new RuntimeException("系统繁忙,请重新访问");
}
//1.查出所有1级分类
/**
* 将数据库的多次查询变成一次
*/
System.out.println("获取分布式锁成功");
//2. 封装数据
Map<String, List<Catelog2Vo>> map = null;
try {
System.out.println("查询了数据库");
List<CategoryEntity> selectList = baseMapper.selectList(null);
List<CategoryEntity> level1Category = selectList.stream().filter(s -> s.getParentCid().equals(0L)).collect(Collectors.toList());
map = level1Category.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {
//1.每一个的一级分类,查到1级分类的所有二级分类
List<CategoryEntity> categoryEntities = selectList.stream().filter(s -> s.getParentCid().equals(v.getCatId())).collect(Collectors.toList());
List<Catelog2Vo> catelog2VoList = categoryEntities.stream().map(c -> {
Catelog2Vo catelog2Vo = new Catelog2Vo();
catelog2Vo.setId(c.getCatId().toString());
catelog2Vo.setName(c.getName());
catelog2Vo.setCatalog1Id(v.getCatId().toString());
List<CategoryEntity> categoryEntities1 = selectList.stream().filter(s -> s.getParentCid().equals(c.getCatId())).collect(Collectors.toList());
List<Catelog2Vo.Catelog3Vo> collect = categoryEntities1.stream().map(c3 -> {
Catelog2Vo.Catelog3Vo catelog3Vo = new Catelog2Vo.Catelog3Vo();
catelog3Vo.setId(c3.getCatId().toString());
catelog3Vo.setName(c3.getName());
catelog3Vo.setCatalog2Id(c.getCatId().toString());
return catelog3Vo;
}).collect(Collectors.toList());
catelog2Vo.setCatalog3List(collect);
return catelog2Vo;
}).collect(Collectors.toList());
return catelog2VoList;
}));
if (map == null) {
/**
* 解决缓存穿透
*/
map = new HashMap<>();
}
redisTemplate.opsForValue().set(CATALOG_JSON, map, Duration.ofDays(1));
} catch (Exception e) {
e.printStackTrace();
} finally {
//lua脚本解锁
//如果获取key等于传过来的值,就执行删除操作,否则就不执行
String script="if redis.call('get',KEYS[1])==ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
Long execute = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList(Lock), uuid);
if (execute==1){
System.out.println("原子删锁成功");
}else {
System.out.println("原子删锁失败");
}
}
return map;
}
只有一个查询了数据库