SpringBoot+Mybatis一級緩存和二級緩存詳解


本文主要介紹在SpringBoot項目中如何使用Mybatis的一級、二級緩存,為了演示方便,本文的數據庫采用H2內存數據庫,數據庫連接池默認使用SpringBoot2.X自帶的hikariCP。
正確的使用Mybatis緩存可以有效減少多余的數據庫查詢操作,節約IO。
接下來我們從實踐出發,看一看mybatis的一級,二級緩存如何使用,相關代碼請查閱:https://github.com/zhengxl5566/springboot-demo.git

1、概念介紹

  • 什么是一級緩存
    在日常開發過程中,經常會有相同的sql執行多次查詢的情況,mybatis提供了一級緩存來優化這些查詢,避免多次請求數據庫。
    一級緩存在mybatis中默認是開啟的並且是session級別,它的作用域為一次sqlSession會話。
  • 什么是二級緩存
    相對於一級緩存,二級緩存的作用域更廣泛,它不止局限於一個sqlSession,可以在多個sqlSession之間共享,事實上,它的作用域是namespace。
    mybatis的二級緩存默認也是開啟的,但由於他的作用域是namespace,所以還需要在mapper.xml中開啟才能生效
  • 緩存的優先級
    通過mybatis發起的查詢,作用順序為:二級緩存->一級緩存->數據庫 ,其中任何一個環節查到不為空的數據,都將直接返回結果
  • 緩存失效
    當在一個緩存作用域中發生了update、insert、delete 動作后,將會觸發緩存失效,下一次查詢將命中數據庫,從而保證不會查到臟數據。

2、代碼演示

一級緩存

默認情況下,mybatis開啟並使用了一級緩存。
單元測試用例:

    /**
     * 開啟事務,測試一級緩存效果
     * 緩存命中順序:二級緩存---> 一級緩存---> 數據庫
     **/
    @Test
    @Transactional(rollbackFor = Throwable.class)
    public void testFistCache(){
        // 第一次查詢,緩存到一級緩存
        userMapper.selectById(1);
        // 第二次查詢,直接讀取一級緩存
        userMapper.selectById(1);

    }

執行結果:

可以看到,雖然進行了兩次查詢,但最終只請求了一次數據庫,第二次查詢命中了一級緩存,直接返回了數據。
這里有兩點需要說明一下:
1、為什么開啟事務
由於使用了數據庫連接池,默認每次查詢完之后自動commite,這就導致兩次查詢使用的不是同一個sqlSessioin,根據一級緩存的原理,它將永遠不會生效。
當我們開啟了事務,兩次查詢都在同一個sqlSession中,從而讓第二次查詢命中了一級緩存。讀者可以自行關閉事務驗證此結論。
2、兩種一級緩存模式
一級緩存的作用域有兩種:session(默認)和statment,可通過設置local-cache-scope 的值來切換,默認為session。
二者的區別在於session會將緩存作用於同一個sqlSesson,而statment僅針對一次查詢,所以,local-cache-scope: statment可以理解為關閉一級緩存。

二級緩存

默認情況下,mybatis打開了二級緩存,但它並未生效,因為二級緩存的作用域是namespace,所以還需要在Mapper.xml文件中配置一下才能使二級緩存生效

  • 單表二級緩存
    下面對userMapper.xml配置一下,讓其二級緩存生效,只需加入cache標簽即可
<cache></cache>

單元測試用例:

    /**
     * 測試二級緩存效果
     * 需要*Mapper.xml開啟二級緩存
     **/
    @Test
    public void testSecondCache(){
        userMapper.selectById(1);
        userMapper.selectById(1);
    }

執行結果:

這里可以看到,第二次查詢直接命中了緩存,日志還打印了該緩存的命中率。讀者可以自行關閉二級緩存查看效果,通過注掉對應mapper.xml的cache標簽,或者 cache-enabled: false 均可

  • 多表聯查二級緩存
    接下來演示多表聯查的二級緩存,user表left join user_order表 on user.id = user_order.user_id
    我們考慮這樣一種情況,該聯查執行兩次,第二次聯查前更新user_order表,如果只使用cache配置,將會查不到更新的user_orderxi,因為兩個mapper.xml的作用域不同,要想合到一個作用域,就需要用到cache-ref
    userOrderMapper.xml
<cache></cache>

userMapper.xml

<cache-ref namespace="com.zhengxl.mybatiscache.mapper.UserOrderMapper"/>

單元測試用例:

    /**
     * 測試多表聯查的二級緩存效果
     * 需要*Mapper.xml設定引用空間
     **/
    @Test
    public void testJoin(){
        System.out.println(userMapper.selectJoin());
        System.out.println(userMapper.selectJoin());
        UserOrder userOrder = new UserOrder();
        userOrder.setGoodName("myGoods");
        userOrder.setUserId(1);
        userOrderMapper.saveOrder(userOrder);
        System.out.println(userMapper.selectJoin());

    }

執行結果:

首先查詢了兩次user表,第二次命中二級緩存,然后更新user_order表,使緩存失效,第三次查詢時命中數據庫。

綜上,mybatis的單機緩存就介紹完了,讀者可以自行下載樣例工程驗證。

總結:

mybatis默認的session級別一級緩存,由於springboot中默認使用了hikariCP,所以基本沒用,需要開啟事務才有用。但一級緩存作用域僅限同一sqlSession內,無法感知到其他sqlSession的增刪改,所以極易產生臟數據
二級緩存可通過cache-ref讓多個mapper.xml共享同一namespace,從而實現緩存共享,但多表聯查時配置略微繁瑣。
所以生產環境建議將一級緩存設置為statment級別(即關閉一級緩存),如果有必要,可以開啟二級緩存

注意:如果應用是分布式部署,由於二級緩存存儲在本地,必然導致查詢出臟數據,所以,分布式部署的應用不建議開啟。

參考資料:


歡迎掃碼關注我的個人公眾號,獲取最新文章↓


免責聲明!

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



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