11 快取
簡介
1、什么是快取 [ Cache ] ?
- 存在記憶體中的臨時資料
- 將用戶經常查詢的資料存放在快取中(記憶體),用戶去查詢資料就不用從磁盤上(關系型資料庫檔案)查詢,從快取中直接查詢,從而提高查詢效率,解決了高并發系統的性能問題,
2、為什么使用快取?
- 減少和資料庫的互動次數,減少系統的開銷,提高系統的效率,
3、什么樣的資料能使用快取?
- 經常查詢并且不經常改變的資料,
MyBatis快取
-
MyBatis包含一個非常強大的查詢快取特性,它可以非常方便地定制和配置快取,快取可以極大的提升查詢效率,
-
MyBatis系統中默認定義了兩級快取:一級快取和二級快取
-
- 默認情況下,只有一級快取開啟,(SqlSession級別的快取,也稱為本地快取)
- 二級快取需要手動開啟和配置,他是基于namespace級別的快取,
- 為了提高擴展性,MyBatis定義了快取介面Cache,我們可以通過實作Cache介面來自定義二級快取
一級快取
一級快取也叫本地快取:
- 與資料庫同一次會話期間查詢到的資料會放在本地快取中;
- 以后如果需要獲取相同的資料,直接從快取中拿,沒必須再去查詢資料庫,
測驗
module:mybatis-08
1、在MyBatis核心組態檔中加入日志,方便測驗結果的查看,
2、撰寫物體類User.java
package com.zzb.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
private int id;
private String name;
private String pwd;
}
3、撰寫UserMapper介面
package com.zzb.dao;
import com.zzb.pojo.User;
import org.apache.ibatis.annotations.Param;
public interface UserMapper {
// 根據Id查詢用戶
User getUserById(@Param("id") int id);
}
4、撰寫UserMapper.xml組態檔
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--configuration-->
<mapper namespace="com.zzb.dao.UserMapper">
<cache/>
<select id="getUserById" resultType="User">
select * from user where id = #{id}
</select>
</mapper>
5、測驗
@Test
public void test2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.getUserById(1);
System.out.println(user);
User user2 = userMapper.getUserById(1);
System.out.println(user == user2);
sqlSession.close();
}
測驗結果:

很顯然,測驗僅從資料庫中查詢了一次,并將結果存入了快取中,
一級快取失效的四種情況
一級快取是SqlSession級別的快取,是一直開啟的,我們關閉不了它;
一級快取失效情況:沒有使用到當前的一級快取,那么還需要再向資料庫中發起一次查詢請求!
1、SqlSession不同
測驗
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
SqlSession sqlSession2 = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
User user = userMapper.getUserById(1);
System.out.println(user);
User user2 = userMapper2.getUserById(1);
System.out.println(user2);
System.out.println(user == user2);
sqlSession.close();
sqlSession2.close();
}
測驗結果:

測驗結果中執行了兩次SQL陳述句,表明每個SqlSession中的快取相互獨立!
2、SqlSession相同,查詢條件不同
測驗
@Test
public void test2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.getUserById(1);
System.out.println(user);
User user2 = userMapper.getUserById(2);
System.out.println(user == user2);
sqlSession.close();
}
測驗結果:

兩個SQL陳述句不同,查詢結果不同,故不會從一級快取中命中結果!
3、SqlSession相同,但兩次查詢之間執行了增刪改操作!
UserMapper介面中增加方法:
// 修改一個用戶
int updateUser(Map map);
UserMapper.xml撰寫:
<update id="updateUser" parameterType="map">
update user set name = #{name} where id = #{id}
</update>
測驗
@Test
public void test3(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user1 = userMapper.getUserById(1);
System.out.println(user1);
Map<Object, Object> map = new HashMap<Object, Object>();
map.put("name", "zb");
map.put("id", 4);
int i = userMapper.updateUser(map);
if(i == 1){
System.out.println("修改資料成功!");
}else{
System.out.println("修改資料失敗!");
}
User user2 = userMapper.getUserById(1);
System.out.println(user2);
System.out.println(user1 == user2);
sqlSession.close();
}
測驗結果:

測驗結果表明,兩次查詢間如果執行了增刪改方法,一級快取會被清空!因為增刪改操作可能會對當前資料產生影響!
4、SqlSession相同,手動清除一級快取
測驗
@Test
public void test2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.getUserById(1);
System.out.println(user);
// 手動清除一級快取
sqlSession.clearCache();
User user2 = userMapper.getUserById(1);
System.out.println(user == user2);
sqlSession.close();
}
測驗結果:

二級快取
-
二級快取也叫全域快取,一級快取作用域太低了,所以誕生了二級快取
-
基于namespace級別的快取,一個名稱空間,對應一個二級快取;
-
作業機制
-
- 一個會話查詢一條資料,這個資料就會被放在當前會話的一級快取中;
- 如果當前會話關閉了,這個會話對應的一級快取就沒了;但是我們想要的是,會話關閉了,一級快取中的資料被保存到二級快取中;
- 新的會話查詢資訊,就可以從二級快取中獲取內容;
- 不同的mapper查出的資料會放在自己對應的快取(map)中;
使用步驟
1、開啟全域快取
在mybatis核心組態檔中添加如下配置:
<setting name="cacheEnabled" value="https://www.cnblogs.com/zzbstudy/archive/2020/12/10/true"/>
2、在每一個Mapper.xml組態檔中使用二級快取
<cache/>
官方示例=====>查看官方檔案
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
這個更高級的配置創建了一個 FIFO 快取,每隔 60 秒重繪,最多可以存盤結果物件或串列的 512 個參考,而且回傳的物件被認為是只讀的,因此對它們進行修改可能會在不同執行緒中的呼叫者產生沖突,
3、代碼測驗
所有物體類先實作序列化介面,
測驗代碼:
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
SqlSession sqlSession2 = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
User user = userMapper.getUserById(1);
System.out.println(user);
// 關閉sqlSession,是該sqlSession中的一級快取的內容存放到二級快取中
sqlSession.close();
User user2 = userMapper2.getUserById(1);
System.out.println(user2);
System.out.println(user == user2);
sqlSession.close();
sqlSession2.close();
}
測驗結果:

- 只要開啟了二級快取,我們在同一個Mapper中的查詢,可以在二級快取中拿到資料
- 查出的資料都會被默認先放在一級快取中
- 只有會話提交或者關閉以后,一級快取中的資料才會轉到二級快取中
快取原理圖

EhCache

第三方快取實作--EhCache;
Ehcache是一種廣泛使用的java分布式快取,用于通用快取;
要在應用程式中使用Ehcache,需要引入依賴的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>
在mapper.xml中使用對應的快取即可
<mapper namespace = “org.acme.FooMapper” >
<cache type = “org.mybatis.caches.ehcache.EhcacheCache” />
</mapper>
撰寫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:是否保存到磁盤,當快取存盤的資料達到maxInMemory限制時
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>
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/232912.html
標籤:其他
上一篇:10 動態SQL
