1. 程式人生 > >mybatis學習筆記(15)-查詢快取之二級快取

mybatis學習筆記(15)-查詢快取之二級快取

本文主要講mybatis的二級快取,二級快取是mapper級別的快取,多個SqlSession去操作同一個Mapper的sql語句,多個SqlSession可以共用二級快取,二級快取是跨SqlSession的。

二級快取原理

二級快取原理

首先開啟mybatis的二級快取.

sqlSession1去查詢使用者id為1的使用者資訊,查詢到使用者資訊會將查詢資料儲存到二級快取中。

如果SqlSession3去執行相同mapper下sql,執行commit提交,清空該mapper下的二級快取區域的資料。

sqlSession2去查詢使用者id為1的使用者資訊,去快取中找是否存在資料,如果存在直接從快取中取出資料。

二級快取與一級快取區別,二級快取的範圍更大,多個sqlSession可以共享一個UserMapper的二級快取區域

UserMapper有一個二級快取區域(按namespace分),其它mapper也有自己的二級快取區域(按namespace分)。每一個namespace的mapper都有一個二快取區域,兩個mapper的namespace如果相同,這兩個mapper執行sql查詢到資料將存在相同的二級快取區域中。

開啟二級快取

mybaits的二級快取是mapper範圍級別,除了在SqlMapConfig.xml設定二級快取的總開關,還要在具體的mapper.xml中開啟二級快取。

在核心配置檔案SqlMapConfig.xml中加入<setting name="cacheEnabled" value="true"/>

設定項 描述 允許值 預設值
cacheEnabled 對在此配置檔案下的所有cache 進行全域性性開/關設定。 true/false true
<!-- 開啟二級快取 -->
<setting name="cacheEnabled" value="true"/>

在UserMapper.xml中開啟二快取,UserMapper.xml下的sql執行完成會儲存到它的快取區域(HashMap)。

<mapper namespace="com.iot.mybatis.mapper.UserMapper">
<!-- 開啟本mapper的namespace下的二級快取-->
<cache /> ... </mapper>

呼叫pojo類實現序列化介面

public class User implements Serializable{
    ....
}

為了將快取資料取出執行反序列化操作,因為二級快取資料儲存介質多種多樣,不一定在記憶體。

測試方法

// 二級快取測試
@Test
public void testCache2() throws Exception {
    SqlSession sqlSession1 = sqlSessionFactory.openSession();
    SqlSession sqlSession2 = sqlSessionFactory.openSession();
    SqlSession sqlSession3 = sqlSessionFactory.openSession();
    // 建立代理物件
    UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
    // 第一次發起請求,查詢id為1的使用者
    User user1 = userMapper1.findUserById(1);
    System.out.println(user1);

    //這裡執行關閉操作,將sqlsession中的資料寫到二級快取區域
    sqlSession1.close();


//      //使用sqlSession3執行commit()操作
//      UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class);
//      User user  = userMapper3.findUserById(1);
//      user.setUsername("張明明");
//      userMapper3.updateUser(user);
//      //執行提交,清空UserMapper下邊的二級快取
//      sqlSession3.commit();
//      sqlSession3.close();



    UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
    // 第二次發起請求,查詢id為1的使用者
    User user2 = userMapper2.findUserById(1);
    System.out.println(user2);

    sqlSession2.close();
}

1.無更新,輸出

DEBUG [main] - Cache Hit Ratio [com.iot.mybatis.mapper.UserMapper]: 0.0
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 103887628.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@631330c]
DEBUG [main] - ==>  Preparing: SELECT * FROM user WHERE id=? 
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <==      Total: 1
User [id=1, username=測試使用者22, sex=2, birthday=null, address=null]
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@631330c]
DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@631330c]
DEBUG [main] - Returned connection 103887628 to pool.
DEBUG [main] - Cache Hit Ratio [com.iot.mybatis.mapper.UserMapper]: 0.5
User [id=1, username=測試使用者22, sex=2, birthday=null, address=null]

2.有更新,輸出

DEBUG [main] - Cache Hit Ratio [com.iot.mybatis.mapper.UserMapper]: 0.0
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 103887628.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@631330c]
DEBUG [main] - ==>  Preparing: SELECT * FROM user WHERE id=? 
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <==      Total: 1
User [id=1, username=測試使用者22, sex=2, birthday=null, address=null]
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@631330c]
DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@631330c]
DEBUG [main] - Returned connection 103887628 to pool.
DEBUG [main] - Cache Hit Ratio [com.iot.mybatis.mapper.UserMapper]: 0.5
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Checked out connection 103887628 from pool.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@631330c]
DEBUG [main] - ==>  Preparing: update user set username=?,birthday=?,sex=?,address=? where id=? 
DEBUG [main] - ==> Parameters: 張明明(String), null, 2(String), null, 1(Integer)
DEBUG [main] - <==    Updates: 1
DEBUG [main] - Committing JDBC Connection [com.mysql.jdbc.JDBC4Connection@631330c]
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@631330c]
DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@631330c]
DEBUG [main] - Returned connection 103887628 to pool.
DEBUG [main] - Cache Hit Ratio [com.iot.mybatis.mapper.UserMapper]: 0.3333333333333333
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Checked out connection 103887628 from pool.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@631330c]
DEBUG [main] - ==>  Preparing: SELECT * FROM user WHERE id=? 
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <==      Total: 1
User [id=1, username=張明明, sex=2, birthday=null, address=null]
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@631330c]
DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@631330c]
DEBUG [main] - Returned connection 103887628 to pool.

useCache配置

在statement中設定useCache=false可以禁用當前select語句的二級快取,即每次查詢都會發出sql去查詢,預設情況是true,即該sql使用二級快取。

<select id="findOrderListResultMap" resultMap="ordersUserMap" useCache="false">

總結:針對每次查詢都需要最新的資料sql,要設定成useCache=false,禁用二級快取。

重新整理快取(就是清空快取)

重新整理快取就是清空快取。在mapper的同一個namespace中,如果有其它insert、update、delete操作資料後需要重新整理快取,如果不執行重新整理快取會出現髒讀。

設定statement配置中的flushCache="true"屬性,預設情況下為true即重新整理快取,如果改成false則不會重新整理。使用快取時如果手動修改資料庫表中的查詢資料會出現髒讀。如下:

<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User" flushCache="true">

總結:一般下執行完commit操作都需要重新整理快取,flushCache=true表示重新整理快取,這樣可以避免資料庫髒讀。

應用場景和侷限性

  • 應用場景

對於訪問多的查詢請求且使用者對查詢結果實時性要求不高,此時可採用mybatis二級快取技術降低資料庫訪問量,提高訪問速度,業務場景比如:耗時較高的統計分析sql、電話賬單查詢sql等。

實現方法如下:通過設定重新整理間隔時間,由mybatis每隔一段時間自動清空快取,根據資料變化頻率設定快取重新整理間隔flushInterval,比如設定為30分鐘、60分鐘、24小時等,根據需求而定。

  • 侷限性

mybatis二級快取對細粒度的資料級別的快取實現不好,比如如下需求:對商品資訊進行快取,由於商品資訊查詢訪問量大,但是要求使用者每次都能查詢最新的商品資訊,此時如果使用mybatis的二級快取就無法實現當一個商品變化時只重新整理該商品的快取資訊而不重新整理其它商品的資訊,因為mybaits的二級快取區域以mapper為單位劃分,當一個商品資訊變化會將所有商品資訊的快取資料全部清空。解決此類問題需要在業務層根據需求對資料有針對性快取。