1. 程式人生 > 其它 >雙重檢測機制解決快取穿透問題

雙重檢測機制解決快取穿透問題

什麼是快取穿透?

當大量併發訪問時,首批併發會在沒有查詢到快取的情況下集體訪問資料庫,造成快取暫時性無效。

話不多說,直接上程式碼,先建立一個執行緒池

  //建立一個執行緒池
        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){} 鎖住了誰?