緩存三劍客解決方案
1.緩存雪崩
定義: 大量緩存數據在同一時間點集體失效,導致所有請求直接穿透到數據庫,引發數據庫瞬時高負載甚至崩潰。
解決方案: 設置過期隨機值,避免大量緩存同時失效。
// 緩存雪崩防護:隨機過期時間 + 雙層緩存// 設置隨機過期時間(基礎時間 + 隨機偏移)Random random = new Random();long expire = baseExpire + random.nextInt(5 * 60 * 1000); // 基礎5分鐘 + 隨機5分鐘內data = loader.load();setCache(key, data, expire);setCache(backupKey, data, expire * 2); // 備份緩存過期時間更長return data;
}
2. 緩存擊穿解決方案
定義: 某個熱點Key突然失效(如過期或被刪除),同時有大量并發請求訪問該Key,導致請求全部發到數據庫。
方案1:互斥鎖(分布式鎖)
@Nullable// todo 3、緩存擊穿 -> 互斥鎖:只能由一個線程進行緩存構建,其他線程等待,吞吐量較低private Shop huchi(Long id) {String shopJsonStr = stringRedisTemplate.opsForValue().get("cache:shop:" + id);if (StrUtil.isNotBlank(shopJsonStr)) {return JSONUtil.toBean(shopJsonStr, Shop.class);}// 未命中獲取鎖String tryLockKey = "cache:shop:lock:" + id;Shop shop = null;try {boolean tryLock = getLock(tryLockKey);// 未命中:不斷休眠直至獲取成功while (!tryLock) {Thread.sleep(50);tryLock = getLock(tryLockKey);}// 獲取互斥鎖,進行緩存的構建shop = getById(id);if (shop == null) {// 數據庫中也不存在時候,進行空字符串緩存stringRedisTemplate.opsForValue().set("cache:shop:" + id, "", 2, TimeUnit.MINUTES);return null;}stringRedisTemplate.opsForValue().set("cache:shop:" + id, JSONUtil.toJsonStr(shop), 2, TimeUnit.MINUTES);} catch (Exception e) {e.getStackTrace();} finally {unLock(tryLockKey);}return shop;}
方案2:邏輯過期(適合高并發讀場景)
@Nullable// todo 3、緩存擊穿 -> 邏輯過期:通過設置邏輯過期時間,然后判斷是否過期來確定是否進行緩存更新private Shop exLogical(Long id) {ExecutorService executorService = Executors.newFixedThreadPool(10);String shopJsonStr = stringRedisTemplate.opsForValue().get("cache:shop:" + id);// 如果不存在那就是一定不存在if (StrUtil.isBlank(shopJsonStr)) {return null;}//RedisDate redisDate = JSONUtil.toBean(shopJsonStr, RedisDate.class);Shop shop = JSONUtil.toBean((JSONObject) redisDate.getObject(), Shop.class);// 未邏輯過期if (redisDate.getEx().isAfter(LocalDateTime.now())) {return shop;}// 邏輯過期// 緩存重建String tryLockKey = "cache:shop:lock:" + id;boolean tryLock = getLock(tryLockKey);if (tryLock) {// 開啟獨立的線程去獨立的進行緩存executorService.submit(() -> {try {this.saveShopRedis(id, 20L);} finally {unLock(tryLockKey);}});}return shop;}// 手動設置邏輯過期時間private void saveShopRedis(Long id, Long ex) {Shop shop = getById(id);RedisDate redisDate = new RedisDate();redisDate.setEx(LocalDateTime.now().plusSeconds(ex));redisDate.setObject(shop);stringRedisTemplate.opsForValue().set("cache:shop:" + id, JSONUtil.toJsonStr(redisDate));}
方案2:布隆過濾器
覺得有幫助的話,點個贊或關注再走唄。
本人水平有限,有錯的地方還請批評指正。
什么是精神內耗?
簡單地說,就是心理戲太多,自己消耗自己。
所謂:
言未出,結局已演千百遍;
身未動,心中已過萬重山;
行未果,假想災難愁不展;
事已閉,過往仍在腦中演。