「Mybatis系列」Mybatis緩存

1. 緩存介紹

Mybatis提供查詢緩存,若是緩存中有數據就不用從數據庫中獲取,用於減輕數據壓力,提升系統性能。java

Mybatis的查詢緩存總共有兩級,咱們稱之爲一級緩存和二級緩存:mysql

  • 一級緩存是SqlSession級別的緩存。在操做數據庫時須要構造 sqlSession對象,在對象中有一個數據結構(HashMap)用於存儲緩存數據。不一樣的sqlSession之間的緩存數據區域(HashMap)是互相不影響的。
  • 二級緩存是Mapper(namespace)級別的緩存。多個SqlSession去操做同一個Mapper的sql語句,多個SqlSession能夠共用二級緩存,二級緩存是跨SqlSession的。

2. 一級緩存

Mybatis默認開啓了一級緩存spring

img

說明:sql

  • 第一次發起查詢用戶id爲1的用戶信息,先去找緩存中是否有id爲1的用戶信息,若是沒有,從數據庫查詢用戶信息,將查詢到的用戶信息存儲到一級緩存中。
  • 若是中間sqlSession去執行commit操做(執行插入、更新、刪除),清空SqlSession中的一級緩存,這樣作的目的爲了讓緩存中存儲的是最新的信息,避免髒讀。
  • 第二次發起查詢用戶id爲1的用戶信息,先去找緩存中是否有id爲1的用戶信息,緩存中有,直接從緩存中獲取用戶信息。

2.1 測試1

@Test
    public void testOneLevelCache() {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        // 第一次查詢ID爲1的用戶,去緩存找,找不到就去查找數據庫
        User user1 = mapper.findUserById(1);
        System.out.println(user1);
        
        // 第二次查詢ID爲1的用戶
        User user2 = mapper.findUserById(1);
        System.out.println(user2);

        sqlSession.close();
    }

2.2 測試2

@Test
    public void testOneLevelCache() {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        // 第一次查詢ID爲1的用戶,去緩存找,找不到就去查找數據庫
        User user1 = mapper.findUserById(1);
        System.out.println(user1);
        
        User user = new User();
        user.setUsername("隔壁老詹1");
        user.setAddress("洛杉磯湖人");
        //執行增刪改操做,清空緩存
        mapper.insertUser(user);
        
        // 第二次查詢ID爲1的用戶
        User user2 = mapper.findUserById(1);
        System.out.println(user2);

        sqlSession.close();
    }

2.3 具體應用

正式開發,是將mybatis和spring進行整合開發,事務控制在service中。數據庫

一個service方法中包括 不少mapper方法調用:緩存

service{
    //開始執行時,開啓事務,建立SqlSession對象
    //第一次調用mapper的方法findUserById(1)
    
    //第二次調用mapper的方法findUserById(1),從一級緩存中取數據
    //方法結束,sqlSession關閉
}

若是是執行兩次service調用查詢相同 的用戶信息,是不走一級緩存的,由於mapper方法結束,sqlSession就關閉,一級緩存就清空。數據結構

3. 二級緩存

3.1 原理

二級緩存是mapper(namespace)級別的。mybatis

img

說明:app

  1. 第一次調用mapper下的SQL去查詢用戶信息。查詢到的信息會存到該mapper對應的二級緩存區域內。
  2. 第二次調用相同namespace下的mapper映射文件中相同的SQL去查詢用戶信息。會去對應的二級緩存內取結果。
  3. 若是調用相同namespace下的mapper映射文件中的增刪改SQL,並執行了commit操做。此時會清空該namespace下的二級緩存。

3.2 開啓二級緩存

Mybatis默認是沒有開啓二級緩存,開啓步驟以下:性能

  1. 在覈心配置文件SqlMapConfig.xml中加入如下內容(開啓二級緩存總開關):
<!-- 開啓二級緩存總開關 -->
<settings>
    <setting name="cacheEnabled" value="true"/>
</settings>
  1. 在UserMapper映射文件中,加入如下內容,開啓二級緩存:
<!-- 開啓本mapper下的namespace的二級緩存,默認使用的是mybatis提供的PerpetualCache -->
<cache></cache>

3.3 實現序列化

因爲二級緩存的數據不必定都是存儲到內存中,它的存儲介質多種多樣,好比說存儲到文件系統中,因此須要給緩存的對象執行序列化。若是該類存在父類,那麼父類也要實現序列化。

3.4 測試1

@Test
    public void testTwoLevelCache() {
        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        SqlSession sqlSession2 = sqlSessionFactory.openSession();
        SqlSession sqlSession3 = sqlSessionFactory.openSession();

        UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
        UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
        UserMapper mapper3 = sqlSession3.getMapper(UserMapper.class);
        // 第一次查詢ID爲1的用戶,去緩存找,找不到就去查找數據庫
        User user1 = mapper1.findUserById(1);
        System.out.println(user1);
        // 關閉SqlSession1
        sqlSession1.close();

        // 第二次查詢ID爲1的用戶
        User user2 = mapper2.findUserById(1);
        System.out.println(user2);
        // 關閉SqlSession2
        sqlSession2.close();
    }

3.5 測試2

@Test
    public void testTwoLevelCache() {
        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        SqlSession sqlSession2 = sqlSessionFactory.openSession();
        SqlSession sqlSession3 = sqlSessionFactory.openSession();

        UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
        UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
        UserMapper mapper3 = sqlSession3.getMapper(UserMapper.class);
        // 第一次查詢ID爲1的用戶,去緩存找,找不到就去查找數據庫
        User user1 = mapper1.findUserById(1);
        System.out.println(user1);
        // 關閉SqlSession1
        sqlSession1.close();

        //修改查詢出來的user1對象,做爲插入語句的參數
        user1.setUsername("隔壁老詹1");
        user1.setAddress("洛杉磯湖人");

        mapper3.insertUser(user1);

        // 提交事務
        sqlSession3.commit();
        // 關閉SqlSession3
        sqlSession3.close();

        // 第二次查詢ID爲1的用戶
        User user2 = mapper2.findUserById(1);
        System.out.println(user2);
        // 關閉SqlSession2
        sqlSession2.close();
    }

3.6 禁用二級緩存

默認二級緩存的粒度是Mapper級別的,可是若是在同一個Mapper文件中某個查詢不想使用二級緩存的話,就須要對緩存的控制粒度更細。

在select標籤中設置useCache=false,能夠禁用當前select語句的二級緩存,即每次查詢都是去數據庫中查詢,默認狀況下是true,即該statement使用二級緩存。

<select id="findUserById" parameterType="int" resultType="com.kkb.mybatis.po.User" useCache="true">
    SELECT * FROM user WHERE id = #{id}

</select>

3.7 刷新二級緩存

經過flushCache屬性,能夠控制select、insert、update、delete標籤的是否屬性二級緩存

默認設置

  • 默認狀況下若是是select語句,那麼flushCache是false。
  • 若是是insert、update、delete語句,那麼flushCache是true。

默認配置解讀

  • 若是查詢語句設置成true,那麼每次查詢都是去數據庫查詢,即意味着該查詢的二級緩存失效。
  • 若是增刪改語句設置成false,即便用二級緩存,那麼若是在數據庫中修改了數據,而緩存數據仍是原來的,這個時候就會出現髒讀。

flushCache設置以下:

<select id="findUserById" parameterType="int"
        resultType="com.kkb.mybatis.po.User" useCache="true" flushCache="true">
        SELECT * FROM user WHERE id = #{id}
</select>

3.8 應用場景

  • 使用場景:

    對於訪問響應速度要求高,可是實時性不高的查詢,能夠採用二級緩存技術。

  • 注意事項:

    在使用二級緩存的時候,要設置一下刷新間隔(cache標籤中有一個flashInterval屬性)來定時刷新二級緩存,這個刷新間隔根據具體需求來設置,好比設置30分鐘、60分鐘等,單位爲毫秒

3.9 侷限性

Mybatis二級緩存對細粒度的數據級別的緩存實現很差。

  • 場景:

    對商品信息進行緩存,因爲商品信息查詢訪問量大,可是要求用戶每次查詢都是最新的商品信息,此時若是使用二級緩存,就沒法實現當一個商品發生變化只刷新該商品的緩存信息而不刷新其餘商品緩存信息,由於二級緩存是mapper級別的,當一個商品的信息發送更新,全部的商品信息緩存數據都會清空。

  • 解決方法

    此類問題,須要在業務層根據須要對數據有針對性的緩存。

    好比能夠對常常變化的 數據操做單獨放到另外一個namespace的mapper中。

相關文章
相關標籤/搜索