spring cache 學習——@CacheEvict 使用詳解


1. 功能說明

  除了填充緩存,spring cache 也支持使用 @CacheEvict 來刪除緩存。@CacheEvict 就是一個觸發器,在每次調用被它注解的方法時,就會觸發刪除它指定的緩存的動作。跟 @Cacheable 和 @CachePut 一樣,@CacheEvict 也要求指定一個或多個緩存,也指定自定義一的緩存解析器和 key 生成器,也支持指定條件(condition 參數)。

  關於這些(value、cacheNames、key、keyGenerator、cacheManager、cacheResolver、condition)屬性參數的用法和說明,請參考:

    spring cache 學習 —— @Cacheable 使用詳解

 

2. 例子

  我們來舉個簡單的例子,首先看一下 mysql 數據,我們想要刪除 name=“微服務測試0修改一下試試” 這條數據(邏輯刪除)

    

 

 

   這條數據在 redis 中已經存在了

    

 

 

   接下來我們編寫一個刪除方法,並使用 @CacheEvict 注解:

@Override
    @CacheEvict(value = "menuById", key = "#id")
    public Boolean deleteById(String id) {
        return this.removeById(id);
    }

  然后在 controller 調用它:

@DeleteMapping("/deleteById/{id}")
    public Boolean deleteById(@PathVariable("id")String id){
        return menuService.deleteById(id);
    }

  接下來,我們請求一下接口,看看結果是否刪除成功的:

    

 

 

   再看看 mysq 數據的 del 字段和 redis 是否還有數據:

    

 

 

     

 

 

   可以看到,mysql 和 緩存都刪除成功了。

 

3. allEntries 參數

  allEntries 是 @CacheEvict 特有的一個屬性,意為是否刪除整個緩存(value 或 cacheNames 指定的),默認為 false。從上述的例子中,我們可以看到,結果只刪除了指定 key 的緩存數據條目,另一個沒有被刪除。現在我們來驗證這個參數。

  首先,我們使用 @Cacheable 或者 @CachePut 讓緩存產生兩條數據:

    

 

 

   接下來,我們將上述刪除方法的 allEntries=true,再請求一遍刪除接口,結果:

    

    

   可以看到,盡管我的接口只指定一條數據刪除,而且 mysql 中也確實只刪除了一條數據,但是緩存中整個都被刪除了,說明 allEntries 起作用了。

 

4. beforeInvocation 參數

  beforeInvocation 是 @CacheEvict 中特有的一個屬性,意為是否在執行對應方法之前刪除緩存,默認 false(即執行方法之后再刪除緩存)。首先思考一個問題,在什么情況下我們需要主動去刪除緩存呢?一般來講都是在刪除數據的時候,需要主動去刪除緩存。那么就存在一個問題,程序執行時順序的,那我們到底是應該先刪除緩存,再調用方法去數據庫中刪除;還是先從數據庫中刪除,完了之后再去刪除對應的緩存呢?在正常情況下,這兩種方式差別並不大,畢竟程序執行都是毫秒級的,順序執行沒有什么時間跨度。但是,現實環境復雜,緩存訪問和 db 訪問都可能會出現異常,這種情況下就有區別了:

      • 如果先刪除緩存成功,然后 db 刪除失敗,那么接下來的查詢就會直達數據庫,造成壓力;
      • 如果先 db 刪除成功,然后刪除緩存失敗,那么就會造成臟緩存;

  至於該如何取舍,spring cache 通過 beforeInvocation 給開發者提供選擇。

  接下來,我們來模擬這兩種情況,直觀地感受一下 beforeInvocation=true 或 false 的區別。雖然我的應用是在本地執行,但是 mysql 和 redis 都是遠程服務器上的,所以可以通過斷開網絡連接的方式來模擬訪問異常的情況。

  首先讓緩存中產生兩條數據

    

    對應的 mysql 數據:

    

 

 

 

  我們利用線程的 sleep 方法來延長執行時間,以便來得及進行斷開網絡的操作。。。。。。。。。。

  4.1. beforeInvocation=false 時,預期的結果應該是:mysql 中 del 變為了 true,但是緩存中仍然有數據。

    首先看下代碼:

@Override
    @CacheEvict(value = "menuById", key = "#id")
    public Boolean deleteById(String id) {
        System.out.println("開始操作 db");
        Boolean result = this.removeById(id);
        System.out.println("db 操作結束");
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return result;
    }

    接下來,我們請求接口刪除 name=“微服務測試0修改一下試試”這條數據。

    因為中途關閉了網絡,所以返回是這樣的:

      

 

 

     打印是這樣的:

      

 

 

     數據庫結果,已經刪除:

      

 

 

     緩存結果,沒有刪除:

      

 

 

     結果符合預期,說明當 beforeInvocation = false 時,是先執行方法,再操作緩存。

  4.2. beforeInvocation = true 時,預期的結果應該是:mysql 中 del 仍然為 false,但是緩存中已經被刪除。

    首先看下代碼:

@Override
@CacheEvict(value = "menuById", key = "#id", beforeInvocation = true)
public Boolean deleteById(String id) {
System.out.println("開始操作 db");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Boolean result = this.removeById(id);
System.out.println("db 操作結束");
return result;
}

    接下來,我們請求接口刪除 name=“微服務測試2” 這條數據。

    返回是這樣的:

      

 

 

     打印是這樣的:

      

 

 

     數據庫結果,沒有刪除:

      

 

 

    緩存結果,已經刪除:

       

 

 

     結果符合預期,說明當 beforeInvocation = true 時,是先操作緩存,再執行方法。

  驗證成功。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM