1. 程式人生 > >【Mybatis框架】查詢快取(二級快取)

【Mybatis框架】查詢快取(二級快取)

       繼上一篇部落格,我們講述了mybatis的一級快取,接下來,我們來學習一下mybatis的二級快取

部落格連結地址: http://blog.csdn.net/liweizhong193516/article/details/53639350 

按照慣例,先來一張形象一點的圖:

       二級快取區域是根據mapper的namespace劃分的,相同namespace的mapper查詢資料放在同一個區域,如果使用mapper代理方法每個mapper的namespace都不同,此時可以理解為二級快取區域是根據mapper劃分,也就是根據名稱空間來劃分的,如果兩個mapper檔案的名稱空間一樣,那樣,他們就可以共享一個mapper快取。

       每次查詢會先從快取區域找,如果找不到從資料庫查詢,查詢到資料將資料寫入快取。

Mybatis內部儲存快取使用一個HashMap,key為hashCode+sqlId+Sql語句。value為從查詢出來對映生成的java物件

       sqlSession執行insert、update、delete等操作commit提交後會清空快取區域。

開啟快取:

在這特別提醒一下,Mybatis的二級快取是需要配置來開啟的,我們需要在Mybatis的核心配置檔案SqlMapConfig.xml中加入:

<setting name="cacheEnabled" value="true"/>

然後還要在Mapper對映檔案中新增一行:

<cache/> <!--表示此mapper開啟二級快取-->
假如說,已開啟二級快取的Mapper中有個statement要求禁用怎麼辦,那也不難,只需要在statement中設定useCache=false就可以禁用當前select語句的二級快取,也就是每次都會生成sql去查詢,ps:預設情況下預設是true,也就是預設使用二級快取
<select id="findOrderListResultMap" resultMap="ordersUserMap" useCache="false">


重新整理快取:

在mapper的同一個namespace中,如果有其他insert、update、delete操作後都需要執行重新整理快取操作,來避免髒讀。這時我們只需要設定statement配置中的flushCache=“true“屬性,就會預設重新整理快取,相反如果是false就不會了。當然,不管開不開快取重新整理功能,你要是手動更改資料庫表,那都肯定不能避免髒讀的發生,那就屬於手賤了。

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

 那既然能夠重新整理快取,能定時重新整理嗎?也就是設定時間間隔來重新整理快取,答案是肯定的。我們在mapper對映檔案中新增<cache/>來表示開啟快取,那接下來,只需要我們在配置flushinterval(重新整理間隔)就哦了:
<cache  eviction="FIFO"  flushInterval="60000"  size="512"  readOnly="true"/>
flushInterval(重新整理間隔)可以被設定為任意的正整數,而且它們代表一個合理的毫秒形式的時間段。預設情況是不設定,也就是沒有重新整理間隔,快取僅僅呼叫語句時重新整理。
size(引用數目)可以被設定為任意正整數,要記住你快取的物件數目和你執行環境的可用記憶體資源數目。預設值是1024。
readOnly(只讀)屬性可以被設定為true或false。只讀的快取會給所有呼叫者返回快取物件的相同例項。因此這些物件不能被修改。這提供了很重要的效能優勢。可讀寫的快取會返回快取物件的拷貝(通過序列化)。這會慢一些,但是安全,因此預設是false。

而這個例子更高階的配置建立了一個 FIFO 快取,並每隔 60 秒重新整理,存數結果物件或列表的 512 個引用,而且返回的物件被認為是隻讀的,因此在不同執行緒中的呼叫者之間修改它們會導致衝突。可用的收回策略有, 預設的是 LRU:

1.      LRU – 最近最少使用的:移除最長時間不被使用的物件。

2.      FIFO – 先進先出:按物件進入快取的順序來移除它們。

3.      SOFT – 軟引用:移除基於垃圾回收器狀態和軟引用規則的物件。

4.      WEAK – 弱引用:更積極地移除基於垃圾收集器狀態和弱引用規則的物件。

這就是我們軟考擅長的東西了,不用解釋了吧!

說了這麼多,擺個例子來結束本篇博文:

//獲取session1
	SqlSession session1 = sqlSessionFactory.openSession();
	UserMapper userMapper = session1.getMapper(UserMapper.class);
//使用session1執行第一次查詢
	User user1 = userMapper.findUserById(1);
	System.out.println(user1);
//關閉session1
	session1.close();
//獲取session2
	SqlSession session2 = sqlSessionFactory.openSession();
	UserMapper userMapper2 = session2.getMapper(UserMapper.class);
//使用session2執行第二次查詢,由於開啟了二級快取這裡從快取中獲取資料不再向資料庫發出sql
	User user2 = userMapper2.findUserById(1);
	System.out.println(user2);
//關閉session2
	session2.close();

執行效果:


而如果我們在1、2之間執行一次commit操作,就變成了:


由此可見,Mybatis的二級快取是跨Session的,每個Mapper享有同一個二級快取域,同樣,每次執行commit操作之後,同樣會清空快取。

Mybatis這麼好,如何應用呢?

       因為這是一種快取機制嘛,只有相對於實時性要求不高的需求才會使用快取機制,它也一樣。對於訪問多的查詢請求且使用者對查詢結果實時性要求不高,此時可採用mybatis二級快取技術降低資料庫訪問量,提高訪問速度,業務場景比如:耗時較高的統計分析sql、電話賬單查詢sql等。
實現方法如下:通過設定重新整理間隔時間,由mybatis每隔一段時間自動清空快取,根據資料變化頻率設定快取重新整理間隔flushInterval,比如設定為30分鐘、60分鐘、24小時等,根據需求而定。

可是,好歸好,Mybatis也有它一定的侷限性。那就是Mybatis對於細粒度的資料級別的快取實現的不是太好,也就是如果同Mapper下的商品類別繁多的話,他不能實現只重新整理某固定商品的資訊,而只能全盤重新整理。當時將這塊的時候我想過讓Mapper水平分割槽不就行了,可是後來說到Mybatis的二級快取是以名稱空間劃分的或者說是以Mapper劃分,不管我們怎麼水平劃分,只要名稱空間一樣,那就只共享一個二級快取域,當重新整理的時候還是會全Mapper更新一遍。