面試經歷

在很長的一段時間裡,我以為快取擊穿和快取穿透是一個東西,直到最近去騰訊面試,面試官問我快取擊穿和穿透的區別;我回答它倆是一樣的,面試官馬上抬起頭用他那細長的單眼皮眼睛瞪著我說:“你確定嗎?”,最後面試提醒我,既然有不同的名字,那他們肯定就是不一樣的,也就是說快取擊穿和快取穿透不是一個東西;

那麼今天我們就看看這倆玩意的區別,以及它們引發的後果;

在專案中加入快取

一般情況下,我們會把熱點資料放到快取中,比如常用的字典、使用者資訊、訂單詳情等等;也就是說,當專案啟動後,先將熱點資料載入到redis中,以後需要資料時就不用每次都去資料庫查詢了,這樣一來,既減少了資料庫的壓力,也提升了訪問速度,可謂是一舉多得呀!

快取穿透

快取穿透是指快取和資料庫中都沒有的資料,而使用者不斷髮起請求,如發起為id為“-1”的資料或id為特別大不存在的資料。這時的使用者很可能是攻擊者,攻擊會導致資料庫壓力過大。

解決方案:

  1. 介面層增加校驗,如使用者鑑權校驗,id做基礎校驗,id<=0的直接攔截;
  2. 從快取取不到的資料,在資料庫中也沒有取到,這時也可以將key-value對寫為key-null,快取有效時間可以設定短一些,如30秒(設定太長會導致正常情況也沒法使用)。這樣可以防止攻擊使用者反覆用同一個id暴力攻擊

快取擊穿

快取擊穿指的是大量的key在同一時間過期,但是又有大量的請求需要用到這些已經過期的key,那麼程式在redis找不到資料,就會去資料庫裡查詢,資料庫處理大量的請求的同時導致壓力瞬間增大,造成壓力過大,甚至導致崩潰;



解決方案

  1. 設定key值永不過期
  2. 將key的過期時間設為隨機
  3. 增加互斥鎖,當多個key過期時,同一時間只有一個查詢請求下發到資料庫,其他的key等待一個個地輪流查,就可以避免資料庫壓力過大的問題;程式碼如下:
    static Lock lock = new ReentrantLock();

    public String getData(String key ) throws InterruptedException {
try {
// 從redis獲取值
String data = getRedisData(key);
// 如果key不存在,從資料庫查詢
if(null == data){
// 嘗試獲取鎖
if(!lock.tryLock()){
// 獲取鎖失敗 ,100ms後在次嘗試
TimeUnit.MILLISECONDS.sleep(100);
data = getData(key);
}
// 走到這裡表示成功獲取鎖 // 從myqsl中獲取鎖
data = getMysqlData(key); // 將資料更新到redis
setDataToRedis(key,value);
}
return data;
} catch (Exception e){
e.printStackTrace();
throw e;
} finally {
// 解鎖
lock.unlock();
}
}

穿透和擊穿的區別

關於穿透和擊穿的區別上面已經介紹的很清楚了,這裡在做個總結

  • 穿透 :大量請求了快取和資料庫中都沒有的資料,每次都查詢資料庫,導致資料庫壓力過大
  • 擊穿 : 大量key在同一時間過期,導致所有請求都達到資料庫,導致資料庫壓力過大

雪崩效應

雪崩效應指的是由穿透和擊穿引起的資料庫壓力過大,最後導致整個資料庫宕機,一旦資料庫崩了,它所帶來的連鎖反應是可怕的,資料庫不可用的情況下你的伺服器也無法使用;這就是雪崩效應;