雙重檢測機制解決快取穿透問題
阿新 • • 發佈:2021-06-18
什麼是快取穿透?
當大量併發訪問時,首批併發會在沒有查詢到快取的情況下集體訪問資料庫,造成快取暫時性無效。
話不多說,直接上程式碼,先建立一個執行緒池
//建立一個執行緒池 ExecutorService executorService = Executors.newFixedThreadPool(4 * 2); for (int i=0; i<5000; i++) { executorService.submit(new Runnable() { @Override public void run() { studentService.getStudentById(1); } }); }
呼叫方法
public Student getStudentById(Integer id) { redisTemplate.setKeySerializer(new StringRedisSerializer()); //查詢快取 Student student = (Student) redisTemplate.opsForValue().get("studentKey"); //判斷快取是否為空 if (null == student) { System.out.println("查詢了資料庫......"); //查詢資料庫 student = studentMapper.selectByPrimaryKey(id); //放入快取 redisTemplate.opsForValue().set("studentKey", student); } else { System.out.println("查詢了快取......"); } return student; }
結果:
顯而易見,在第一批併發下的第一個查詢還沒存入redis的時候,後面幾個執行緒已經去找資料庫的要完資料了。如果第一批併發體量很大,資料庫就有可能崩潰。
怎麼解決哪?
第一個解決方案:
在方法上加synchronized,讓他們排隊訪問。
執行輸出:
這個解決方案是有效的,但是這個解決方案存在明顯的弊端,效率慢到姥姥家了。5w個併發請求,跑了好幾分鐘。
嘗試第二個解決方案:
public /*synchronized*/ Student getStudentById(Integer id) { redisTemplate.setKeySerializer(new StringRedisSerializer()); //查詢快取 Student student = (Student) redisTemplate.opsForValue().get("studentKey"); //判斷快取是否為空 if (null == student) { //雙重檢測鎖實現 synchronized (this) { student = (Student) redisTemplate.opsForValue().get("studentKey"); if (null == student) { System.out.println("查詢了資料庫......"); //查詢資料庫 student = studentMapper.selectByPrimaryKey(id); //放入快取 redisTemplate.opsForValue().set("studentKey", student); } } } else { System.out.println("查詢了快取......"); } return student; }
執行結果:
效率大大的提升上來了,這就是雙重檢測機制,那麼問題來了。
問問各位小夥伴,這個synchornzied(this){} 鎖住了誰?