8、查詢緩存(二級緩存)(mybatis筆記)

一、原理

1
  • 第一次SqlSession1去查詢用戶id為1的用戶信息,查詢到用戶信息將會將查詢到的數據存儲到二級緩存中。

  • 第二次SqlSession2去查詢用戶id為1的用戶信息,先去緩存中找,如果緩存有則直接從緩存中取數據,如果沒有則去數據庫中查詢。

  • 二級緩存與一級緩存的區別,二級緩存的范圍更大,多個SqlSession可以共享一個UserMapper的二級緩存區域。UserMapper有一個二級緩存區域(這個是按照namespace劃分),其他mapper也有自己的二級緩存區域(按照namespace劃分)。每一個namespacemapper有一個二級緩存區域。也就是說如果兩個mappernamespace相同,那么這兩個mapper執行sql查詢到的數據將存儲在一個二級緩存區域中。

  • 當然我們如果在兩次查詢之間加入提交操作,則同樣會清空二級緩存的。這樣第二次查詢的時候也要發出sql去數據庫中查詢。

二、測試

  • 開啟二級緩存(工程mybatis13
    除了在SqlMapConfig.xml中開啟二級緩存的總開關,還要在具體的mapper.xml中開啟二級緩存。
    SqlMapConfig.xml
<settings>
    <!-- 打開延遲加載的開關,再將積極加載改為消極加載(即按需加載) -->
    <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="aggressiveLazyLoading" value="false"/>
    <!-- 開啟二級緩存,這里設置是為了方便管理,本身默認是開啟的 -->
    <setting name="cacheEnabled" value="true"/>
</settings>

UserMapper.xml

<!-- 開啟本mapper的namespace下的二級緩存 -->
<cache />

說明:其實在全局配置文件中配置主要是為了便于管理。在UserMapper.xml中開啟二級緩存,UserMapper.xml下的sql執行完成會存儲到它的緩存區域(HashMap)。

索引 描述 允許值 默認值
cacheEnabled 對在此配置文件下的所有cache進行全局性開/關設置 true/false true
  • 設置pojo類實現序列換接口,為了將緩存數據取出執行反序列化操作,因為二級緩存數據存儲介質多種多樣,不一定在內存中。
    User.java
public class User implements Serializable{
    private Integer id ;
    private String username ;
    private String sex ;
    private Date birthday ;
    private String address ;
    //用戶創建的訂單列表
    private List<Orders> ordersList ;
....
}
  • 測試
    UserMapperTest.java
//二級緩存測試
@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);
    sqlSession1.close();//如果不關閉,SqlSession數據是不能寫到二級緩存區域的

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

此時的測試結果為:

2

可以看到總共只是發出了一條sql語句,同時注意到有一項信息Cache Hit Retio,這表示緩存命中率,第一次的時候緩存中沒有數據則命中率是0.0,第二次先從一級緩存中找,沒有;再從二級緩存中找,找到了,此時緩存命中率是0.5。

  • 加入提交
//二級緩存測試
@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);
    sqlSession1.close();//如果不關閉,SqlSession數據是不能寫到二級緩存區域的
    
    //執行提交操作
    UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class);   
    User user = userMapper3.findUserById(1);
    user.setUsername("狗蛋");
    userMapper3.updateUser(user);
    sqlSession3.commit();//執行提交清空UserMapper下的二級緩存
    sqlSession3.close();

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

}

此時我們在測試的時候就會發現發出了兩條查詢語句。

三、二級緩存其他一些參數

3.1 禁用二級緩存

  • statement中設置userCache="false"可以禁用當前select語句的二級緩存,即每次查詢都會發出sql去查詢,默認情況是true,即該sql使用二級緩存。
<select id="findOrderListResultMap" resultMap="ordersUserMap" userCache = "false">

總結:針對每次查詢都需要最新的數據,要設置成禁用二級緩存。

3.2 刷新緩存

  • mapper的同一個namespace中,如果有其它的insert、update、delete操作數據后需要刷新緩存,如果不執行,則緩存中可能出現臟數據。

  • 設置statement中的flushCache="true"屬性,默認情況下為true,即刷新緩存,如果改成false則不會刷新。使用緩存時如果手動修改數據庫表中的查詢數據會出現臟讀。如下:

<insert id="insertUser" parameterType="User" flushCache="true">

注意:這里刷新緩存就是清空緩存。
總結:一般情況下,執行完commit操作都需要刷新緩存,flushCache="true"表示刷新緩存,這樣可以避免數據庫臟讀。

3.3 Mybatis Cache參數

  • flushInterval(刷新間隔)可以被設置為任意的正整數,而且它們代表一個合理的毫秒形成的時間段。默認情況下,也就是沒有刷新問題,緩存僅僅調用語句時刷新。
  • size(引用數目)可以被設置為任意正整數,要記住你緩存的對象數目和你運行環境的可用內存資源數目。默認值是1024
  • readOnly(只讀)屬性可以被設置為truefalse。只讀的緩存會給所有的調用者返回緩存對象的相同示例。因此這些對象不能被修改。這提供了很重要的性能優勢。可讀寫的緩存會返回緩存對象的拷貝(通過序列化)。這會慢一些,但是安全,因此默認是false
    如下例子:
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>

這個更高級的配置創建了一個FIFO緩存,并每隔60秒刷新,存儲結果對象或列表的512個引用,而且返回的對象被認為是只讀,因此在不同線程中的調用者之間修改它們會導致沖突。可用的解決策略有,默認的是LRU

  • 1.LRU:最近最少使用的:移除最長時間不被使用的對象
  • 2.FIFO:先進先出:按對象進入緩存的順序來移除它們
  • 3.SOFT: 軟引用:移除基于垃圾回收器狀態和軟引用規則的對象。
  • 4.WEAK : 弱引用:更積極地移除基于垃圾收集器狀態和弱引用規則的對象。

3.4 整合EhCache

EhCache是一個分布式的緩存框架。即使我們使用mybatis的二級緩存,查詢出來的相關數據也只是保存在單個服務器上,所以我們需要使用分布式的緩存。

  • 導入相關的jar包:(工程mybatis14
ehcache-core-2.6.8.jar
mybatis-ehcache-1.0.3.jar
  • 加入EhCache的配置文件config/ehcache.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
    <diskStore path="E:\ehcache" />
    <defaultCache 
        maxElementsInMemory="10000" 
        eternal="false"
        overflowToDisk="false"
        timeToIdleSeconds="120" 
        timeToLiveSeconds="120" 
        maxElementsOnDisk="10000000"
        diskExpiryThreadIntervalSeconds="120" 
        memoryStoreEvictionPolicy="LRU">
    </defaultCache>
</ehcache>
  • 在相關的UserMapper.xml文件中進行配置:
<!-- 開啟本mapper的namespace下的二級緩存,
type指定cache接口的實現類的接口,mybatis默認使用PerpetualCache類,我們要和
EhCache整合,需要配置type為EhCache的實現cache接口的類 -->
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>

之前我們只是開啟了二級緩存,默認是使用mybatis的二級緩存,這里我們配置使用EhCache緩存。

  • 測試
    測試文件不需要改動,直接測試即可,我們發現還是可以實現二級緩存的效果。

四、二級緩存的應用場景

  • 對于訪問多的查詢請求且用戶對查詢結果實時性要求不高,此時可采用mybatis的二級緩存技術降低數據庫的訪問量,提高訪問速度,業務場景比如:耗時較高的統計分析sql、電話賬單查詢sql等。

  • 實現方法如下:通過設置刷新間隔時間,由mybatis每隔一段時間自動清空緩存,根據數據變化頻率設置緩存刷新間隔flushInterval,比如設置為30分鐘、60分鐘等,根據需求而定。

五、局限性

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • MyBatis--查詢緩存 查詢緩存的使用,主要是為了提高查詢訪問速度。將用戶對同一數據的重復查詢過程簡化,不再每...
    我可能是個假開發閱讀 3,058評論 3 13
  • 1. 二級緩存的原理 前面介紹了,mybatis中的二級緩存是mapper級別的緩存,值得注意的是,不同的mapp...
    我相信你愛過gg閱讀 700評論 0 4
  • MyBatis的配置和使用原理 MyBatis 是支持定制化 SQL、存儲過程以及高級映射的優秀的持久層框架。My...
    王帥199207閱讀 939評論 0 4
  • 1 緩存介紹# MyBatis支持聲明式數據緩存(declarative data caching)。當一條SQL...
    七寸知架構閱讀 2,171評論 2 51
  • 非本人總結的筆記,抄點筆記復習復習。感謝傳智博客及黑馬程序猿成長 關聯查詢 數據中的表結構 數據庫的分析方法 第一...
    鍵盤瞎閱讀 1,100評論 3 5