读redis->双重检测同步锁->防止Redis缓存击穿

一般用redis做数据缓存,查询缓存时先查询缓存,没有再查下数据,然后更新缓存。

这里会存在一个问题:并发情况10000个用户查询缓存,缓存失效,那么10000个用户都怼到数据库了,会造成很大压力,也就是缓存击穿,不是穿透。

击穿与穿透区别:

穿透:缓存无(压根没有),数据库无

击穿:缓存无(缓存失效),数据库有

相同点:大量请求怼到数据库

按理说:10000请求,都判断无缓存,那么如果只有一个线程能继续执行,(加锁)继续取读到数据库数据,然后更新缓存,这时其他线程等待。之后,其他线程就可以读取缓存了。

好处:1.防止大量请求到数据库 2.避免重复更新缓存

代码如下:

@Slf4j
@Component
public class CacheTemplateService {

    @Autowired
    private RedisManager redisManager;

    public  T fetchCache(String key, Integer expire, TypeReference clazz, CacheLoadable 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);
    }

}
List resLiveVOS = cacheTemplateService
			.fetchCache(RedisEnum.ADVANCE_LIVE_KEY.key, RedisEnum.ADVANCE_LIVE_KEY.expired,
					new TypeReference>() {
					}, new CacheLoadable>() {
					    @Override
					    public List load() {
						return selectAdvanceLive(uid);
					    }
					});

参考:

https://blog.csdn.net/hjl021/article/details/79168783

缓存穿透、缓存并发、缓存失效之思路变迁 | 并发编程网 – ifeve.com

你可能感兴趣的:(redis,redis,缓存,数据库)