缓存击穿,双缓存处理策略

 

 

微信公众号:molashaonian

缓存击穿

某一个热点 key,在缓存过期的一瞬间,同时有大量的请求打进来,由于此时缓存过期了,所以请求最终都会走到数据库,造成瞬时数据库请求量大、压力骤增,甚至可能打垮数据库。

场景:商品列表,商品详情展示销量,库存等

大多解决方案

  • 加互斥锁。在并发的多个请求中,只有第一个请求线程能拿到锁并执行数据库查询操作,其他的线程拿不到锁就阻塞等着,等到第一个线程将数据写入缓存后,直接走缓存。

  • 热点数据不过期。直接将缓存设置为不过期,然后由定时任务去异步加载数据,更新缓存。

缺点:两种方案问题是能解决,但不是很漂亮,会产生阻塞,增加额外处理资源问题

双缓存处理策略

对于热点 key 可以设置一级,二级缓存;二级缓存可以设置较长的过期时间(甚至不过期),当一级缓存过期时,加锁从数据库中获取最新值,并且更新一级,二级缓存;同时没获得锁的线程直接读取二级缓存返回。

请求 一级缓存 二级缓存 MySQL 查询 缓存失效,加锁获取最新值,并且更新一级,二级缓存 查询 缓存失效,获取锁失败,读取二级缓存 更新 更新 请求 一级缓存 二级缓存 MySQL

 

 

代码实现

public List<GoodsRespDTO> findGoodsList() {
    String key = "page:homepage:goods";
	List<GoodsRespDTO> respDto = myRedis.get(key);
	if(respDto != null) {
		return respDto;
	}
	// 双缓存机制,加锁更新缓存
    Lock lock = myRedis.getLock(key + ":update");
    if (lock.tryLock(-1, 5000)) {
        try {
        		// 查询数据库逻辑
                respDto = selectMySQL();
                if(respDto != null) {
                    // 更新一级缓存
                    myRedis.set(key, respDto, 3000);
                    // 更新二级缓存
                    myRedis.set(key + ":L2Cache", respDto, 8500);
                }
                return respDto;
            }
        } finally {
            lock.unLock();
        }
    }
    // 二级缓存
    return myRedis.get(key + ":L2Cache");
}

微信公众号:molashaonian
Alt

posted on 2022-05-06 18:09  EvanLong  阅读(100)  评论(0编辑  收藏  举报

导航