再談緩存擊穿問題

    在高併發場景中,爲了減少數據庫的壓力,咱們會引入緩存。java

    一個簡單的查詢操做,以下所示:數據庫

   

    僞代碼以下:緩存

public Object getObjById(Integer objId){
      String key = "Lock:Obj:"+objId;
      //先從緩存中獲取數據
      Obj obj= DistributeCacheUtil.get(key);
      if(obj!=null){
            //若是緩存中存在則直接返回緩存中數據
            return obj;
      }
      //若是緩存中不存在該數據,從數據庫中查詢
      obj= DbUtil.getObjById(objId);
      if(obj!=null){
         //若是 數據庫中存在,將查詢出來的數據放入緩存,設置過時時間
         DistributeCacheUtil.set("Lock:Obj:"+objId,obj,1000);
      }
      //返回
      return obj;
}

    但是這裏出現一個問題,好比秒殺場景或者一些其餘場景,某一時刻開始進行秒殺,這時候大量的請求會請求到後臺,若是沒有事先進行數據緩存預熱,這個時候緩存裏是沒有數據的,那麼大量的請求回去查詢數據庫,這樣整個緩存就失去意義了。併發

    如何解決?分佈式

    1.提早進行數據緩存預熱,好比項目啓動時,或者定時器,先將數據查詢出來放到緩存。高併發

    2.加入鎖,具體僞代碼邏輯以下所示:線程

public Object getObjById(Integer objId){
      String key = "Lock:Obj:"+objId;
      //先從緩存中獲取數據
      Obj obj= DistributeCacheUtil.get(key);
      if(obj!=null){
            //若是緩存中存在則直接返回緩存中數據
            return obj;
      }
      //若是緩存中不存在該數據
      //加鎖,這裏分爲阻塞鎖和非阻塞鎖
      if(DistributeCacheUtil.tryLock(key)){
           //進鎖之後先從緩存中獲取數據
           //若是第一個線程持鎖後,加數據加載進緩存後,後來進來的線程能夠直接從緩存中拿
           obj= DistributeCacheUtil.get(key);
           if(obj!=null){
              //若是緩存中存在則直接返回緩存中數據
              return obj;
           }
           //若是緩存中仍是不存在,再從數據庫中拿
           obj= DbUtil.getObjById(objId);
           if(obj!=null){
             //若是數據庫中存在,將查詢出來的數據放入緩存,設置過時時間
             DistributeCacheUtil.set("Lock:Obj:"+objId,obj,1000);
           }
           //返回
           return obj; 
      }else{
           //這裏是處理分佈式鎖爲非阻塞鎖的狀況,若是是阻塞式鎖,這裏就沒必要要處理了
           //這裏根據實際狀況處理,好比直接返回null(秒殺場景,直接返回賣完了)
           //或者休眠1秒鐘,再從緩存中拿
           //或者自旋去查詢緩存
           return null;
      }
}
相關文章
相關標籤/搜索