1. 程式人生 > 其它 >高併發下的快取失效問題

高併發下的快取失效問題

快取擊穿

    1.   對於一些設定了過期時間的 key, 如果這些 key 可能還在某些時間點被超高併發地訪問,是一種非常熱點的資料(比如: 某某明星吃瓜事件等)
    2.   如果這個 key 在大量請求進來時正好失效,那麼這些 key 就會穿過快取區訪問資料庫
    3.        解決方法:加併發鎖,在大量併發請求下,只讓一個人去查,而其他人進行等待,查到之後釋放鎖,其他人再去獲取鎖,先查快取,就會有資料,不用去 db
    4. 在單體應用中加鎖,使用 synchronized
sychronized(this){
        String catalogJSON = redisTemplate.opsForValue().get("catalogJSON");
        
if(!StringUtils.isEmpty(catalogJSON)){   // 加鎖後,再次查詢快取   // 如果快取不為 null, 直接返回 Map<String, List<CatalogVo>> result = JSON.parseObject(catalogJSON, new TypeReference<Map<String, Object>>(){}); // 查到的結果,應該立即放入快取,以防鎖釋放後,其他執行緒獲取鎖,但是從快取中還沒讀到資料,又會再次查詢資料庫,導致的時序問題帶來的查了兩次資料庫問題
String s = JSON.toJSONString(catalogJsonFromDb); redisTemplate.opsForValue().set("catalogJSON", s, 1, TimeUnit.DAYS); return result;   }

快取穿透

    1.   在快取中查詢一個不存在的資料,由於快取沒有被命中,就會去查詢資料庫,資料庫中也不存在此條記錄,那麼每次的請求都會越過快取區查詢資料庫,快取就失去了作為快取的意義。
    2.         帶來的問題:
      1.   當請求併發量很大的時候,資料庫的壓力增大的,最終可能導致崩潰
    3.         解決方法:
      1.  將該查詢的key ,給它一個 value 為 null 的值,並加入短暫的過期時間,以防後面資料庫有該值了,這個資料一直佔著。
      2. // 從快取中獲取資料
        String catalogJson = redisTemplate.opsForValue().get("catalogJSON");
        // 判斷資料是否是空的
        if(StringUtils.isEmpty(catalogJSON)){
        // 如果為空,那麼查詢資料庫
        Map<String, List<CatalogVO>> catalogJsonFromDb = getCatalogJsonFromDb();
        // 將查到的資料再放入快取,將物件轉為 json 放在快取中
        String s = JSON.toJSONString(catalogJsonFromDb);
        redisTemplate.opsForValue().set("catalogJSON", 0);
        return catalogJsonFromDb;
        }
        

快取雪崩

    1.   設定快取時 key 採用相同的過期時間,導致快取在某一時刻同時失效,請求因此進入資料庫,導致資料庫壓力過大。
    2.   解決方法: 設定的過期時間儘量不要一樣,可以在一個基礎的失效時間上增加一個隨機值(1-5 分鐘左右), 這樣可以有效避免快取大量失效。
    3. // 從快取中獲取資料
      String catalogJson = redisTemplate.opsForValue().get("catalogJSON");
      // 判斷資料是否是空的
      if(StringUtils.isEmpty(catalogJSON)){
      // 如果為空,那麼查詢資料庫
      Map<String, List<CatalogVO>> catalogJsonFromDb = getCatalogJsonFromDb();
      // 將查到的資料再放入快取,將物件轉為 json 放在快取中
      String s = JSON.toJSONString(catalogJsonFromDb); 
      redisTemplate.opsForValue().set("catalogJSON", s, 1, TimeUnit,DAYS); // 時間按實際需要定義
      return catalogJsonFromDb;
      }