一,场景 redis双检锁
并发访问,获取redis缓存时,先查询缓存,无则查询数据库。
存在一个问题,当并发量比较大时,都查询缓存无数据,都会怼到数据库中,造成数据库压力。
二,代码如下
分析一下代码块中部分逻辑
1.再次查询缓存
2.查询数据库,写入缓存
执行流程
假如1000个请求过来,调用1000次CacheTemplateService.fetchCache,都没有查询到缓存数据,将要执行代码块逻辑,因为加了关键字synchronized,1000个请求代码顺序执行,第一个请求进入,再次查询缓存还是没有查询到数据,查库,写缓存。如果再过来1000个请求,第一次查询缓存,就可以查到数据,不会执行代码块逻辑(主要目的)。
这时原来先进入到1000个请求中的后续999个在排队的请求,会依次执行代码块,先查缓存,再查库,试想一下,如果代码块中没有再次查询缓存的逻辑,还是有大量请求怼到数据库,这就是为啥加双检锁。
@Slf4j
@Component
public class CacheTemplateService {
@Autowired
private RedisManager redisManager;
public <T> T fetchCache(String key, Integer expire, TypeReference<T> clazz, CacheLoadable<T> cacheLoadable) {
String json = (String) redisManager.get(key);
if (Objects.nonNull(json) && StringUtils.isNotBlank(json) && !json.equalsIgnoreCase("null")
&& !"[]".equals(json) && !"{}".equals(json)) {
return JSON.parseObject(json, clazz);
}
synchronized (this) {
json = (String) redisManager.get(key);
if (Objects.nonNull(json) && StringUtils.isNotBlank(json) && !json.equalsIgnoreCase("null")
&& !"[]".equals(json) && !"{}".equals(json)) {
return JSON.parseObject(json, clazz);
}
// 核心业务
T result = cacheLoadable.load();
redisManager.put(key, expire, JSON.toJSONString(result));
return result;
}
}
public void invalidate(String key) {
redisManager.remove(key);
}
}