JAVA面試題之緩存擊穿、緩存穿透、緩存雪崩的三者區(qū)別
前端發(fā)起一個請求,經歷過三次握手后連接到服務器,想要獲取相應的數據,那么服務器接入了緩存中間件后,從接收到Request到最后的Response,到底是怎樣的一個流程呢?以下探討忽略掉參數校驗等邏輯,直接講最核心的鏈路。
調用鏈路
一個請求Request過來,服務器首先和緩存中間件建立連接,傳輸對應key到緩存中間件中獲取相對應的數據,服務器拿到返回的結果后,判斷返回的結果是否有數據,如果有數據,則返回從緩存中拿到的結果。如果緩存中間件中沒有數據,則建立數據庫連接,訪問數據庫服務器,按照相應邏輯拿到返回結果,判斷結果中是否有數據,如果有則返回對應數據,如果沒有則按照業(yè)務場景要求,返回對應結果(一般為null或者new一個空對象)。
緩存擊穿
含義:
什么是緩存擊穿?通俗的講指的是緩存中沒有數據,但數據庫中有數據的場景。那為什么緩存中會沒有數據呢?一般是由于設置了緩存時間導致緩存過期,所以沒有數據。那緩存找不到數據去數據庫查詢就好了呀,為啥又叫擊穿?是因為要查詢這個key對應的數據是一個熱點數據,并發(fā)訪問的量大,同時去查詢數據庫,導致數據庫壓力驟增,嚴重會打崩數據庫。
解決方案:
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、訪問數據庫也拿不到數據后,可以按照具體業(yè)務要求,在緩存層加上一個該key的值,設置一個過期時間,比如10s或者1min等。那為什么不設不過期呢?第一個是說因為該key可能有對應的業(yè)務含義,有可能只是該時間點還沒有數據,所以不能設置不過期;第二個是說如果真的是惡意訪問,那么可能過一段時間就沒有類似請求,那么我們沒有必要一直把該數據留在緩存里。
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; } // 處理對應邏輯 } }
緩存雪崩
含義:
緩存雪崩指的是在同一個時間點,緩存中的大批量數據過期,并且還都是熱點數據,導致同一時間并發(fā)壓力都打到了數據庫中,導致數據庫壓力驟增,甚至宕機。有的人就會問了,這和緩存擊穿不是一個意思嗎?緩存擊穿指的是并發(fā)查詢某條熱點key數據,緩存雪崩指的是大批量。出現場景之一是在某些核心頁面,該頁面的內容都放入了緩存,并且都設置了同樣的緩存時間。
解決方案:
1、最簡單的就是設置熱點數據不過期,但要結合對應業(yè)務場景來看。
2、在給每個熱點key設置過期時間時,加上一個隨機值,使得熱點數據離散開來,不會同一時間大批量過期。
3、使用緩存擊穿場景講到的互斥鎖、分布式鎖。
以上就是JAVA面試題之緩存擊穿、緩存穿透、緩存雪崩的三者區(qū)別的詳細內容,更多關于JAVA 的資料請關注腳本之家其它相關文章!
相關文章
SpringMVC xml文件路徑在web.xml中的配置方式
這篇文章主要介紹了SpringMVC xml文件路徑在web.xml中的配置方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09