缓存的一个常见使用方式:先查缓存--未命中--查DB--写缓存。
if(!cacheUtil.getDataFromCache()){ //(1) Object data = cacheUtil.getDataFromDB();//(2) cacheUtil.setDataToCache(data); }
这种方式使用起来简单,但也存在比较大的问题。我们都知道缓存会设置一定的过期时间,一旦缓存过期,如果此时正好有大量的请求进来,会在代码(1)处出现大量并发,代码(2)处会使DB的压力瞬间增大。为了避免这样的情况出现,我们采取了以下措施:
1.定期主动刷新缓存。使用定时任务来定时更新缓存,定时任务时间比缓存时间短,保证缓存永不过期;这种方案对于key值比较少且固定的数据来说比较适用。
2.对缓存失效时的请求进行限流;1W个请求只处理先进来的100,后面的直接失败;
AtomicInteger cacheLock = new AtomicInteger(0); if(!cacheUtil.getDataFromCache()){ if(cacheLock.incrementAndGet()<=100){ try{ Object data = cacheUtil.getDataFromDB(); cacheUtil.setDataToCache(data); }finally{ cacheLock.decrementAndGet(); } }else{ return; } }
这个方案虽然控制了穿透到DB的请求数量,但是会导致大量请求失败,所以方案可以改成让一定数量的请求去走DB,而让其他请求等待。