JAVA面試題之緩存擊穿、緩存穿透、緩存雪崩的三者區別
前端發起一個請求,經歷過三次握手後連接到服務器,想要獲取相應的數據,那麼服務器接入瞭緩存中間件後,從接收到Request到最後的Response,到底是怎樣的一個流程呢?以下探討忽略掉參數校驗等邏輯,直接講最核心的鏈路。
調用鏈路
一個請求Request過來,服務器首先和緩存中間件建立連接,傳輸對應key到緩存中間件中獲取相對應的數據,服務器拿到返回的結果後,判斷返回的結果是否有數據,如果有數據,則返回從緩存中拿到的結果。如果緩存中間件中沒有數據,則建立數據庫連接,訪問數據庫服務器,按照相應邏輯拿到返回結果,判斷結果中是否有數據,如果有則返回對應數據,如果沒有則按照業務場景要求,返回對應結果(一般為null或者new一個空對象)。
緩存擊穿
含義:
什麼是緩存擊穿?通俗的講指的是緩存中沒有數據,但數據庫中有數據的場景。那為什麼緩存中會沒有數據呢?一般是由於設置瞭緩存時間導致緩存過期,所以沒有數據。那緩存找不到數據去數據庫查詢就好瞭呀,為啥又叫擊穿?是因為要查詢這個key對應的數據是一個熱點數據,並發訪問的量大,同時去查詢數據庫,導致數據庫壓力驟增,嚴重會打崩數據庫。
解決方案:
1、如果是不改變的數據,如一些常量值,則可以設置對應熱點key永不過期。
2、加上互斥鎖,防止同一臺服務器同一時間有多個連接訪問數據庫。
// 偽代碼 public class Main { // 雙重檢測鎖 public static String getHotData(String key) { // 先從緩存中間件獲取對應熱點key數據 String response = redis.get(key); // 緩存沒有數據 if(Objects.isNull(response)) { // 保證一臺服務器同一時間隻有一個線程訪問 synchronized (Main.class) { // 假設A線程訪問進synchronized裡,線程B, C阻塞在synchronsized外面 // 線程A退出synchronized後,線程B和C應該從redis中拿而不是再訪問數據庫 response = redis.get(key); // 訪問數據庫 拿到數據後 寫進redis中 if(Objects.isNull(response)) { response = loadDataFromMySQL(key); redis.set(key, response); } } } return response; } }
3、加上分佈式鎖,全局保證隻有一個線程訪問數據庫。
// 偽代碼 public class Main { // 分佈式唯一key public static String getHotData(String key, int tryTime) throws InterruptedException { if(tryTime >= 4) { return ""; } // 先從緩存中間件獲取對應熱點key數據 String response = redis.get(key); // 緩存沒有數據 if(Objects.isNull(response)) { // 保證整個服務集群同一時間隻有一個線程訪問 if (redis.tryLock()) { try { // 訪問數據庫 拿到數據後 寫進redis中 if(Objects.isNull(response)) { response = loadDataFromMySQL(key); redis.set(key, response); } } finally { redis.unlock(); } } else { TimeUnit.MILLISECONDS.sleep(100); getHotData(key, tryTime + 1); } } return response; } }
緩存穿透
含義:
緩存穿透指的是緩存中間件和數據庫都沒有對應的數據,但是不斷接收到請求獲取該key的數據,導致數據庫壓力過大,甚至崩潰。
解決方案:
1、訪問數據庫也拿不到數據後,可以按照具體業務要求,在緩存層加上一個該key的值,設置一個過期時間,比如10s或者1min等。那為什麼不設不過期呢?第一個是說因為該key可能有對應的業務含義,有可能隻是該時間點還沒有數據,所以不能設置不過期;第二個是說如果真的是惡意訪問,那麼可能過一段時間就沒有類似請求,那麼我們沒有必要一直把該數據留在緩存裡。
2、增加校驗,如果是不符合預期的請求可以直接過濾,比如說緩存中存放瞭用戶信息,對應的緩存key是和id有關系,那麼如果你的id都是大於等於0的,對於小於0的id可以直接做過濾。
@Controller public class Controller { @RequestMapping(value="/test") public String printHello(Integer id) { if(Objects.isNull(id) || id < 0) { return null; } // 處理對應邏輯 } }
緩存雪崩
含義:
緩存雪崩指的是在同一個時間點,緩存中的大批量數據過期,並且還都是熱點數據,導致同一時間並發壓力都打到瞭數據庫中,導致數據庫壓力驟增,甚至宕機。有的人就會問瞭,這和緩存擊穿不是一個意思嗎?緩存擊穿指的是並發查詢某條熱點key數據,緩存雪崩指的是大批量。出現場景之一是在某些核心頁面,該頁面的內容都放入瞭緩存,並且都設置瞭同樣的緩存時間。
解決方案:
1、最簡單的就是設置熱點數據不過期,但要結合對應業務場景來看。
2、在給每個熱點key設置過期時間時,加上一個隨機值,使得熱點數據離散開來,不會同一時間大批量過期。
3、使用緩存擊穿場景講到的互斥鎖、分佈式鎖。
以上就是JAVA面試題之緩存擊穿、緩存穿透、緩存雪崩的三者區別的詳細內容,更多關於JAVA 的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- 死磕 java同步系列之synchronized解析
- Java中常用的設計模式之單例模式詳解
- Java實現二叉堆、大頂堆和小頂堆
- Java中List.of()和Arrays.asList()的區別及原因分析
- 手寫一個@Valid字段校驗器的示例代碼