1. 程式人生 > 其它 >Mybatis全解-07

Mybatis全解-07

快取

什麼是快取

存在記憶體中的資料。將使用者經常查詢的資料放在快取(記憶體)中,使用者去查詢資料就不用每次都從磁碟上查詢,可一衝快取中查詢,從而提高查詢的效率,解決了高併發系統的效能問題。

為什麼要使用快取

減少和資料庫的互動次數,較少系統開銷,提高系統效率。

哪些資料可一使用快取

經常查詢並且不經常改變的資料。

Mybatis快取

可以非常方便地定製和配置快取,極大的提升查詢的效率。

Mybatis系統中預設定義了兩級快取:一級快取和二級快取。

在預設的情況下,只有一級快取開啟(SqlSession級別的快取,也稱為本地快取)。二級快取需要手動開啟和配置,它是基於namespace級別的快取。為了提高擴充套件性,Mybatis定義了快取介面Cache,我們可以通過實現Cache介面來自定義二級快取。

一級快取

一級快取也叫本地快取,與資料庫同一次會話期間查詢到的資料會放在本地快取中,後面如果需要獲取相同的資料,直接從快取中拿,沒必要再去查詢資料庫。

編寫測試環境:注意開啟日誌觀察輸出

//根據id查詢使用者
User queryUserById(@Param("id") int id);
<select id="queryUserById" resultType="user">
  select * from user where id = #{id}
</select>
@Test
public void testQueryUserById(){
   SqlSession session 
= MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.queryUserById(1); System.out.println(user); User user2 = mapper.queryUserById(1); System.out.println(user2); System.out.println(user==user2); session.close(); }

觀察日誌輸出可以看到,兩次的查詢的user物件是同一個。

一級快取失效的四種情況

一級快取是SqlSession級別的快取,是一直開啟的,我們關閉不了它。

1.第一種失效情況:sqlSession不同。

@Test
public void testQueryUserById(){
   SqlSession session = MybatisUtils.getSession();
   SqlSession session2 = MybatisUtils.getSession();
   UserMapper mapper = session.getMapper(UserMapper.class);
   UserMapper mapper2 = session2.getMapper(UserMapper.class);

   User user = mapper.queryUserById(1);
   System.out.println(user);
   User user2 = mapper2.queryUserById(1);
   System.out.println(user2);
   System.out.println(user==user2);

   session.close();
   session2.close();
}

觀察日誌輸出可發現,一共傳送了兩條sql語句

結論:每個sqlSession中的快取相互獨立。

2.第二種失效情況:sqlSession相同,查詢條件不同

@Test
public void testQueryUserById(){
   SqlSession session = MybatisUtils.getSession();
   UserMapper mapper = session.getMapper(UserMapper.class);
   UserMapper mapper2 = session.getMapper(UserMapper.class);

   User user = mapper.queryUserById(1);
   System.out.println(user);
   User user2 = mapper2.queryUserById(2);
   System.out.println(user2);
   System.out.println(user==user2);

   session.close();
}

觀察日誌輸出可發現,一共傳送了兩條sql語句

結論:不同的查詢條件,當前快取中不存在第二次查詢的資料

3.第三種失效的情況:sqlSession相同,兩次查詢之間執行了增刪改操作

@Test
public void testQueryUserById(){
   SqlSession session = MybatisUtils.getSession();
   UserMapper mapper = session.getMapper(UserMapper.class);

   User user = mapper.queryUserById(1);
   System.out.println(user);

   HashMap map = new HashMap();
   map.put("name","fuyao");
   map.put("id",4);
   mapper.updateUser(map);

   User user2 = mapper.queryUserById(1);
   System.out.println(user2);

   System.out.println(user==user2);

   session.close();
}

觀察日誌輸出可發現,查詢在中間執行了增刪改操作後,重新執行了

結論:增刪改操作會對當前資料產生影響

4.第四種失效情況:手動清除一級快取,session.clearCache();

@Test
public void testQueryUserById(){
   SqlSession session = MybatisUtils.getSession();
   UserMapper mapper = session.getMapper(UserMapper.class);

   User user = mapper.queryUserById(1);
   System.out.println(user);

   session.clearCache();//手動清除快取

   User user2 = mapper.queryUserById(1);
   System.out.println(user2);

   System.out.println(user==user2);

   session.close();
}

一級快取就是一個map。

二級快取

二級快取也叫全域性快取,一級快取作用域太低了,所以誕生了二級快取。基於namespace級別的快取,一個名稱空間,對應一個二級快取。

工作機制

一個會話查詢一條資料,這個資料就會被放在當前會話的一級快取中。如果當前會話關閉了,這個會話對應的一級快取就沒了。但是我們要的是,會話關閉了,一級快取中的資料被儲存到二級快取中。新的會話查詢資訊,就可以從二級快取中獲取內容。不同的mapper查出的資料會被放在自己對應的快取(map)中。

使用二級快取步驟

1.在mybatis核心配置檔案中開啟二級快取

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

2.在每個mapper.xml中配置使用二級快取,這個配置非常簡單。可以檢視官方文件

<cache/>

官方示例=====>檢視官方文件
<cache
 eviction="FIFO"
 flushInterval="60000"
 size="512"
 readOnly="true"/>
這個更高階的配置建立了一個 FIFO 快取,每隔 60 秒重新整理,最多可以儲存結果物件或列表的 512 個引用,而且返回的物件被認為是隻讀的,因此對它們進行修改可能會在不同執行緒中的呼叫者產生衝突。

3.測試

@Test
public void testQueryUserById(){
   SqlSession session = MybatisUtils.getSession();
   SqlSession session2 = MybatisUtils.getSession();

   UserMapper mapper = session.getMapper(UserMapper.class);
   UserMapper mapper2 = session2.getMapper(UserMapper.class);

   User user = mapper.queryUserById(1);
   System.out.println(user);
   session.close();

   User user2 = mapper2.queryUserById(1);
   System.out.println(user2);
   System.out.println(user==user2);

   session2.close();
}

結論:只要開啟了二級快取,我們在同一個mapper中查詢,可以在二級快取中拿到資料,查出的資料都會先預設放在一級快取中,只有會話被提交或者關閉之後,一級快取中的資料才會轉到二級快取中。

擴充套件:第三方快取實現-EhCache

EhCache是一種廣泛使用的java分散式快取,用於通用快取;

使用步驟

1.匯入jar包

<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
<dependency>
   <groupId>org.mybatis.caches</groupId>
   <artifactId>mybatis-ehcache</artifactId>
   <version>1.1.0</version>
</dependency>

2.在mapper.xml檔案中使用對應的快取即可

<mapper namespace = “org.acme.FooMapper” > 
   <cache type = “org.mybatis.caches.ehcache.EhcacheCache” /> 
</mapper>

3.編寫ehcache.xml檔案,如果在載入時未找到/ehcache.xml資源或出現問題,則將使用預設配置。

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
        updateCheck="false">
   <!--
      diskStore:為快取路徑,ehcache分為記憶體和磁碟兩級,此屬性定義磁碟的快取位置。引數解釋如下:
      user.home – 使用者主目錄
      user.dir – 使用者當前工作目錄
      java.io.tmpdir – 預設臨時檔案路徑
    -->
   <diskStore path="./tmpdir/Tmp_EhCache"/>
   
   <defaultCache
           eternal="false"
           maxElementsInMemory="10000"
           overflowToDisk="false"
           diskPersistent="false"
           timeToIdleSeconds="1800"
           timeToLiveSeconds="259200"
           memoryStoreEvictionPolicy="LRU"/>

   <cache
           name="cloud_user"
           eternal="false"
           maxElementsInMemory="5000"
           overflowToDisk="false"
           diskPersistent="false"
           timeToIdleSeconds="1800"
           timeToLiveSeconds="1800"
           memoryStoreEvictionPolicy="LRU"/>
   <!--
      defaultCache:預設快取策略,當ehcache找不到定義的快取時,則使用這個快取策略。只能定義一個。
    -->
   <!--
     name:快取名稱。
     maxElementsInMemory:快取最大數目
     maxElementsOnDisk:硬碟最大快取個數。
     eternal:物件是否永久有效,一但設定了,timeout將不起作用。
     overflowToDisk:是否儲存到磁碟,當系統當機時
     timeToIdleSeconds:設定物件在失效前的允許閒置時間(單位:秒)。僅當eternal=false物件不是永久有效時使用,可選屬性,預設值是0,也就是可閒置時間無窮大。
     timeToLiveSeconds:設定物件在失效前允許存活時間(單位:秒)。最大時間介於建立時間和失效時間之間。僅當eternal=false物件不是永久有效時使用,預設是0.,也就是物件存活時間無窮大。
     diskPersistent:是否快取虛擬機器重啟期資料 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
     diskSpoolBufferSizeMB:這個引數設定DiskStore(磁碟快取)的快取區大小。預設是30MB。每個Cache都應該有自己的一個緩衝區。
     diskExpiryThreadIntervalSeconds:磁碟失效執行緒執行時間間隔,預設是120秒。
     memoryStoreEvictionPolicy:當達到maxElementsInMemory限制時,Ehcache將會根據指定的策略去清理記憶體。預設策略是LRU(最近最少使用)。你可以設定為FIFO(先進先出)或是LFU(較少使用)。
     clearOnFlush:記憶體數量最大時是否清除。
     memoryStoreEvictionPolicy:可選策略有:LRU(最近最少使用,預設策略)、FIFO(先進先出)、LFU(最少訪問次數)。
     FIFO,first in first out,這個是大家最熟的,先進先出。
     LFU, Less Frequently Used,就是上面例子中使用的策略,直白一點就是講一直以來最少被使用的。如上面所講,快取的元素有一個hit屬性,hit值最小的將會被清出快取。
     LRU,Least Recently Used,最近最少使用的,快取的元素有一個時間戳,當快取容量滿了,而又需要騰出地方來快取新的元素的時候,那麼現有快取元素中時間戳離當前時間最遠的元素將被清出快取。
  -->

</ehcache>

寫在最後

熟悉使用快取來儲存資料,提升程式效能。