1. 程式人生 > 其它 >14.快取(一級快取和二級快取)

14.快取(一級快取和二級快取)

快取可以將資料儲存在記憶體中,是網際網路系統常常用到的。目前流行的快取伺服器有 MongoDB、Redis、Ehcache 等。快取是在計算機記憶體上儲存的資料,讀取時無需再從磁碟讀入,因此具備快速讀取和使用的特點。

和大多數持久化框架一樣,MyBatis 提供了一級快取和二級快取的支援。預設情況下,MyBatis 只開啟一級快取。

一、一級快取

一級快取是基於 PerpetualCache(MyBatis自帶)的 HashMap 本地快取,作用範圍為 session 域內。當 session flush(重新整理)或者 close(關閉)之後,該 session 中所有的 cache(快取)就會被清空。

在引數和 SQL 完全一樣的情況下,我們使用同一個 SqlSession 物件呼叫同一個 mapper 的方法,往往只執行一次 SQL。因為使用 SqlSession 第一次查詢後,MyBatis 會將其放在快取中,再次查詢時,如果沒有重新整理,並且快取沒有超時的情況下,SqlSession 會取出當前快取的資料,而不會再次傳送 SQL 到資料庫。

由於 SqlSession 是相互隔離的,所以如果你使用不同的 SqlSession 物件,即使呼叫相同的 Mapper、引數和方法,MyBatis 還是會再次傳送 SQL 到資料庫執行,返回結果。

1.1 示例

在 WebsiteMapper 類中新增 selectWebsiteById 方法,程式碼如下。

  • public Website selectWebsiteById(int id);

WebsiteMapper.xml 中新增相應的對映 SQL 語句,程式碼如下。

  • <select id="selectWebsiteById"
  • resultType="net.biancheng.po.Website">
  • SELECT * FROM website
  • WHERE id=#{id}
  • </select>

測試程式碼如下。

package net.biancheng.test;


import java.io.IOException;
import java.io.InputStream;
import java.util.List;


import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.log4j.Logger;


import net.biancheng.po.Website;


public class Test {
public static Logger logger = Logger.getLogger(Test.class);
public static void main(String[] args) throws IOException {


InputStream config = Resources.getResourceAsStream("mybatis-config.xml"); // 根據配置檔案構建
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(config);
SqlSession ss = ssf.openSession();


Website site = ss.selectOne("net.biancheng.mapper.WebsiteMapper.selectWebsiteById", 1);
logger.debug("使用同一個sqlsession再執行一次");
Website site2 = ss.selectOne("net.biancheng.mapper.WebsiteMapper.selectWebsiteById", 1);
// 請注意,當我們使用二級快取的時候,sqlSession呼叫了 commit方法後才會生效
ss.commit();


logger.debug("現在建立一個新的SqlSeesion物件在執行一次");
SqlSession ss2 = ssf.openSession();
Website site3 = ss2.selectOne("net.biancheng.mapper.WebsiteMapper.selectWebsiteById", 1);
// 請注意,當我們使用二級快取的時候,sqlSession呼叫了 commit方法後才會生效
ss2.commit();
}
}

執行結果如下。

DEBUG [main] - ==>  Preparing: SELECT * FROM website WHERE id=?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <==      Total: 1
DEBUG [main] - 使用同一個sqlsession再執行一次
DEBUG [main] - 現在建立一個新的SqlSeesion物件在執行一次
DEBUG [main] - ==>  Preparing: SELECT * FROM website WHERE id=?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <==      Total: 1

從執行結果可以看出,第一個 SqlSession 實際只發生過一次查詢,而第二次查詢就從快取中取出了,也就是 SqlSession 層面的一級快取。為了克服這個問題,我們往往需要配置二級快取,使得快取在 SqlSessionFactory 層面上能夠提供給各個 SqlSession 物件共享。

二、二級快取

二級快取是全域性快取,作用域超出 session 範圍之外,可以被所有 SqlSession 共享。

一級快取快取的是 SQL 語句,二級快取快取的是結果物件。

2.1 二級快取的配置

1)MyBatis 的全域性快取配置需要在 mybatis-config.xml 的 settings 元素中設定,程式碼如下。

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

2)在 mapper 檔案(如 WebMapper.xml)中設定快取,預設不開啟快取。需要注意的是,二級快取的作用域是針對 mapper 的 namescape 而言,即只有再次在 namescape 內(net.biancheng.WebsiteMapper)的查詢才能共享這個快取,程式碼如下。

  • <mapper namescape="net.biancheng.WebsiteMapper">
  • <!-- cache配置 -->
  • <cache
  • eviction="FIFO"
  • flushInterval="60000"
  • size="512"
  • readOnly="true" />
  • ...
  • </mapper>

以上屬性說明如下。

屬性 說明
eviction 代表的是快取回收策略,目前 MyBatis 提供以下策略。
  • LRU:使用較少,移除最長時間不用的物件;
  • FIFO:先進先出,按物件進入快取的順序來移除它們;
  • SOFT:軟引用,移除基於垃圾回收器狀態和軟引用規則的物件;
  • WEAK:弱引用,更積極地移除基於垃圾收集器狀態和弱引用規則的物件。
flushInterval 重新整理間隔時間,單位為毫秒,這裡配置的是 100 秒重新整理,如果省略該配置,那麼只有當 SQL 被執行的時候才會重新整理快取。
size 引用數目,正整數,代表快取最多可以儲存多少個物件,不宜設定過大。設定過大會導致記憶體溢位。這裡配置的是 1024 個物件。
readOnly 只讀,預設值為 false,意味著快取資料只能讀取而不能修改,這樣設定的好處是可以快速讀取快取,缺點是沒有辦法修改快取。


3)在 mapper 檔案配置支援 cache 後,如果需要對個別查詢進行調整,可以單獨設定 cache,程式碼如下。

  • <select id="getWebsiteList" resultType="net.biancheng.po.Website" usecache="true">
  • ...
  • </select>

對於 MyBatis 快取僅作了解即可,因為面對一定規模的資料量,內建的 Cache 方式就派不上用場了,並且對查詢結果集做快取並不是 MyBatis 所擅長的,它專心做的應該是 SQL 對映。對於快取,採用 OSCache、Memcached 等專門的快取伺服器來做更為合理。