快取
1.什么是快取?
想想我們之前所有的查詢最后都要連接資料庫,然而連接資料庫很耗資源!
然后我們要想辦法解決: 我們想能不能一次查詢的結果,給他暫存在一個可以直接取到的地方,這個地方一般在記憶體里!
放在記憶體的這一些查詢的資料就叫快取
我們再次查詢相同資料的時候,直接走快取,就不用走資料庫了
- 存在記憶體中的臨時資料,
- 將用戶經常查詢的資料放在快取(記憶體)中,用戶去查詢資料就不用從磁盤上(關系型資料庫資料檔案)查詢,從快取中查詢,從而提高查詢效率,解決了高并發系統的性能問題,
2.為什么使用快取?
- 減少和資料庫的互動次數,減少系統開銷,提高系統效率,
3.什么樣的資料能使用快取?
- 經常查詢并且不經常改變的資料,【可以使用快取】
- 不經常查詢且經常改變的資料,【不可以使用快取】
Mybatis快取
- Mybatis包含一個非常強大的查詢快取特性,它可以非常方便地定制和配置快取,快取可以極大地提升查詢效率,
- Mybatis系統中默認定義了兩級快取: 一級快取和二級快取
- 默認情況下,只有一級快取開啟,(SqlSession級別地快取,也稱為本地快取)
- 二級快取需要手動開啟和配置,他是基于namespace級別的快取,
- 為了提高擴展性,Mybatis定義了快取介面Cache,我們可以通過實作Cache介面來自定義二級快取,
一級快取:
- 一級快取也叫本地快取:SqlSession
- 與資料庫同一次會話期間查詢到的資料會放在本地快取中,
- 以后如果需要獲取相同的資料,直接從快取中拿,沒必要再去查詢資料庫,
注意:一級快取也就是從拿到SqlSession開始,到SqlSession關閉之間存在!
測驗步驟:
1.開啟日志!
2.測驗在一個SqlSession中查詢兩次相同記錄
@Test
public void test(){
//一級快取開始<<=================
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
System.out.println("=========================");
User user2 = mapper.queryUserById(1);
System.out.println(user2);
System.out.println(user == user2);
sqlSession.close();
//一級快取結束========================>>
}
3.查看日志輸出

一級快取失效的情況:
1.查詢不同的用戶,一級快取失效

2.增刪改操作,可能會改變原來的資料,所以必定會重繪快取!
下面這段代碼,第一次查詢id為1的用戶,中間更新了id=2的用戶資訊,再查一次id為1的用戶,結果顯示不會走一級快取,還是會查詢資料庫!
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
mapper.updateUser(new User(2,"aaa","bbb"));
System.out.println("=========================");
User user2 = mapper.queryUserById(1);
System.out.println(user2);
System.out.println(user == user2);
sqlSession.close();
}

3.查詢不同的mapper.xml
這個就不用測驗了,不同的mapper肯定不會有一級快取!
4.手動清理快取
下面手動清理快取sqlSession.clearCache(),查詢id為1的用戶資訊走了兩次資料庫,沒有走快取,這樣也會使快取失效!
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
//手動清理快取
sqlSession.clearCache();
System.out.println("=========================");
User user2 = mapper.queryUserById(1);
System.out.println(user2);
System.out.println(user == user2);
sqlSession.close();
}

小結:一級快取默認是開啟的,只在一次SqlSession中有效,也就是拿到連接到關閉連接這個區間段!
一級快取就是一個map集合,往map里面放東西,取的時候從map里面取!
二級快取:
-
二級快取也叫全域快取,一級快取作用域太低了,所以誕生了二級快取
-
基于namespace級別的快取,一個名稱空間,對應一個二級快取
-
作業機制
-
一個會話查詢一條資料,這個資料就會被放在當前會話的一級快取中
-
如果當前會話關閉了,這個會話對應的一級快取就沒了,但是我們想要的是,會話關閉了,一級快取中的資料被保存到二級快取中
-
新的會話查詢資訊,就可以從二級快取中獲取內容
-
不同的mapper查出的資料會放在自己對應的快取(map)中
-
測驗步驟:
1.在mybatis-config.xml核心組態檔里顯式地開啟全域快取!(也即是二級快取),雖然默認Mybatis是開啟的,但我們最好寫一下!
<!--顯式地開啟全域快取-->
<setting name="cacheEnabled" value="https://www.cnblogs.com/laiyw/p/true"/>
2.在要使用二級快取的Mapper中開啟
<!--在當前Mapper.xml中使用二級快取-->
<cache />
也可以自定義一些引數:
<!--在當前Mapper.xml中使用二級快取-->
<cache eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
3.測驗
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
SqlSession sqlSession2 = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
User user2 = mapper2.queryUserById(1);
System.out.println(user2);
sqlSession.close();
sqlSession2.close();
}
mapper.xml中不加二級快取的開啟

不加測驗結果,走了兩次資料庫:

mapper.xml檔案加上二級快取的配置:
如下:當第一個會話關閉的時候,將資料快取到了二級快取中,一個會話死了,致使另一個會話也能從快取中拿到資料,快取能在不同的會話中取到,作用域提升了一級,所以才叫二級快取!(當然前提是在同一個mapper里面的方法)
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
SqlSession sqlSession2 = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
sqlSession.close();
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
User user2 = mapper2.queryUserById(1);
System.out.println(user2);
System.out.println(user == user2);
sqlSession2.close();
}

出現的問題:
如果mapper.xml檔案這樣配置二級快取:
<!--在當前Mapper.xml中使用二級快取-->
<cache/>
測驗會報如下這個序列化錯誤:

因此我們需要為物體類實作序列化介面:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
private int id;
private String name;
private String pwd;
}

注意:最后的為false是因為一級快取內容存到了二級快取中,所以記憶體地址變化對比為false!
小結:
- 只要開啟了二級快取,在同一個Mapper下就有效
- 所有的資料都會先放在一級快取中,只有當會話提交或者關閉的時候,才會提交到二級快取中!
快取原理
Mybatis的快取原理:先看二級快取中有沒有快取的資料,在看一級快取有沒有,如果都沒有再走查詢資料庫!

自定義快取-ehcache:
除了上門一級,二級快取之外,我們可以自定義快取實作,但是我們不自己寫,我們參考一個第三方寫好的自定義快取ehcache!
Ehcache是一種廣泛使用的開源Java分布式快取,主要面向通用快取
使用步驟:
要在我們的程式中使用ehcache,先要導包!
<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.1</version>
</dependency>
并且新建一個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:默認快取策略,當ehcache找不到定義的快取時,則使用這個快取策略,只能定義一個,
-->
<defaultCache
eternal="false"
maxElementsInMemory="10000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="259200"
memoryStoreEvictionPolicy="LRU"/>
<!--
name:快取名稱.
maxElementsInMemory:快取最大數目
maxElementsOnDisk:硬碟最大快取個數
eternal:物件是否永久有效,一旦設定了,timeout將不起作用,
overflowToDisk:當系統宕機時,是否保存到磁盤
diskPersistent:是否快取虛擬機重啟期資料
timeToIdleSeconds:設定物件在失效前的允許閑置時間
timeToLiveSeconds:設定物件在失效前允許存活時間
memoryStoreEvictionPolicy:可選清除物件策略:LRU(最近最少使用,默認策略)、FIFO(先進先出)、LFU(最少訪問次數),
-->
<cache
name="cloud_user"
eternal="false"
maxElementsInMemory="5000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="1800"
memoryStoreEvictionPolicy="LRU"/>
</ehcache>
然后再對應mapper.xml檔案中按照如下寫:
<!--在當前Mapper.xml中使用ehcache自定義快取-->
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
然后測驗效果跟一級、二級快取沒啥區別:

最后提一句: 我們在作業中會用Redis資料庫做快取!
碼云地址:https://gitee.com/mo18/Mybatis-Study.git 這篇文章在mybatis-09模塊!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/283013.html
標籤:Java
