帶你深入理解MyBatis緩存機制
一、簡介
1、緩存機制介紹
當客戶端發起一次查詢請求時,首先通過java程序進行網絡傳輸訪問mysql數據庫及對應的數據的服務器硬盤,當第二次的請求也是查詢相同的數據時再通過這個流程顯然有點“浪費”上次請求訪問到的資源,所以我們將第一次查詢到的數據存到緩存區域,當發生下一次相同請求時直接在緩存區域拿就行瞭。
2. 一級緩存和二級緩存
①使用順序
查詢的順序是:
- 先查詢二級緩存,因為二級緩存中可能會有其他程序已經查出來的數據,可以拿來直接使用。
- 如果二級緩存沒有命中,再查詢一級緩存
- 如果一級緩存也沒有命中,則查詢數據庫
- SqlSession關閉之前,一級緩存中的數據會寫入二級緩存
②效用范圍
一級緩存:SqlSession級別二級緩存:SqlSessionFactory級別
它們之間范圍的大小參考下面圖:
二、一級緩存
當使用相同查詢條件查詢數據時,一共隻打印瞭一條SQL語句,兩個變量指向同一個對象。
一級緩存失效的情況:
- 不是同一個SqlSession
- 同一個SqlSession但是查詢條件發生瞭變化
- 同一個SqlSession兩次查詢期間執行瞭任何一次增刪改操作
- 同一個SqlSession兩次查詢期間手動清空瞭緩存
- 同一個SqlSession兩次查詢期間提交瞭事務
三、二級緩存
3.1 mybatis自帶的二級緩存
3.1.1 代碼測試二級緩存
① 開啟二級緩存功能
在想要使用二級緩存的Mapper配置文件中加入cache標簽
<mapper namespace="com.zengchuiyu.mybatis.dao.EmployeeMapper"> <!-- 啟動二級緩存功能 --> <cache/>
②讓實體類支持序列化
public class Employee implements Serializable {
③junit測試
這個功能的測試操作需要將SqlSessionFactory對象設置為成員變量
public class CacheTest { private SqlSessionFactory factory; @Before public void init() throws IOException { factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml")); } //測試二級緩存,(mybatis自帶的) @Test public void test1(){ SqlSession session = factory.openSession(); EmployeeMapper mapper = session.getMapper(EmployeeMapper.class); Employee employee = mapper.selectEmpById(2); System.out.println("employee = " + employee); //在執行第二次查詢前,關閉當前SqlSession session.close(); //開啟新的SqlSession session = factory.openSession(); mapper = session.getMapper(EmployeeMapper.class); employee = mapper.selectEmpById(2); System.out.println("employee = " + employee); session.close(); } }
打印效果:
22:48:18.669 [main] DEBUG com.zengchuiyu.mybatis.dao.EmployeeMapper – Cache Hit Ratio [com.zengchuiyu.mybatis.dao.EmployeeMapper]: 0.5
④緩存命中率
日志中打印的Cache Hit Ratio叫做緩存命中率
Cache Hit Ratio [com.atguigu.mybatis.EmployeeMapper]: 0.0(0/1) Cache Hit Ratio [com.atguigu.mybatis.EmployeeMapper]: 0.5(1/2) Cache Hit Ratio [com.atguigu.mybatis.EmployeeMapper]: 0.6666666666666666(2/3) Cache Hit Ratio [com.atguigu.mybatis.EmployeeMapper]: 0.75(3/4) Cache Hit Ratio [com.atguigu.mybatis.EmployeeMapper]: 0.8(4/5)
緩存命中率=命中緩存的次數/查詢的總次數
3.1.2 查詢結果存入二級緩存的時機
結論:SqlSession關閉的時候,一級緩存中的內容會被存入二級緩存
3.1.3 二級緩存相關配置
- eviction屬性:緩存回收策略
LRU(Least Recently Used) – 最近最少使用的:移除最長時間不被使用的對象。
FIFO(First in First out) – 先進先出:按對象進入緩存的順序來移除它們。
SOFT – 軟引用:移除基於垃圾回收器狀態和軟引用規則的對象。
WEAK – 弱引用:更積極地移除基於垃圾收集器狀態和弱引用規則的對象。
默認的是 LRU。
- flushInterval屬性:刷新間隔,單位毫秒
默認情況是不設置,也就是沒有刷新間隔,緩存僅僅調用語句時刷新
- size屬性:引用數目,正整數
代表緩存最多可以存儲多少個對象,太大容易導致內存溢出
- readOnly屬性:隻讀,true/false
true:隻讀緩存;會給所有調用者返回緩存對象的相同實例。因此這些對象不能被修改。這提供瞭很重要的性能優勢。
false:讀寫緩存;會返回緩存對象的拷貝(通過序列化)。這會慢一些,但是安全,因此默認是 false。
四、整合EHCache
4.1 EHCache簡介
官網地址:https://www.ehcache.org/
Ehcache is an open source, standards-based cache that boosts performance, offloads your database, and simplifies scalability. It’s the most widely-used Java-based cache because it’s robust, proven, full-featured, and integrates with other popular libraries and frameworks. Ehcache scales from in-process caching, all the way to mixed in-process/out-of-process deployments with terabyte-sized caches.
Ehcache是一個開源的,基於標準的緩存,可以提高性能,卸載數據庫,簡化可伸縮性。它是最廣泛使用的基於java的緩存,因為它健壯、可靠、功能齊全,並與其他流行的庫和框架集成。Ehcache從進程內緩存擴展到具有tb大小緩存的進程內/進程外混合部署。
4.2 整合操作
①Mybatis環境
在Mybatis環境下整合EHCache,前提當然是要先準備好Mybatis的環境。
②添加依賴
依賴信息:
<!-- Mybatis EHCache整合包 --> <dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-ehcache</artifactId> <version>1.2.1</version> </dependency> <!-- slf4j日志門面的一個具體實現 --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency>
依賴傳遞情況:
各主要jar包作用
jar包名稱 | 作用 |
---|---|
mybatis-ehcache | Mybatis和EHCache的整合包 |
ehcache | EHCache核心包 |
slf4j-api | SLF4J日志門面包 |
logback-classic | 支持SLF4J門面接口的一個具體實現 |
③整合EHCache
[1]創建EHCache配置文件
ehcache.xml
[2]文件內容
<?xml version="1.0" encoding="utf-8" ?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd"> <!-- 磁盤保存路徑 --> <diskStore path="D:\zengchuiyu\ehcache"/> <defaultCache maxElementsInMemory="1000" maxElementsOnDisk="10000000" eternal="false" overflowToDisk="true" timeToIdleSeconds="120" timeToLiveSeconds="120" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> </defaultCache> </ehcache>
引入第三方框架或工具時,配置文件的文件名可以自定義嗎?
可以自定義:文件名是由我告訴其他環境
不能自定義:文件名是框架內置的、約定好的,就不能自定義,以避免框架無法加載這個文件
④加入logback日志
存在SLF4J時,作為簡易日志的log4j將失效,此時我們需要借助SLF4J的具體實現logback來打印日志。
[1]各種Java日志框架簡介
門面:
名稱 | 說明 |
---|---|
JCL(Jakarta Commons Logging) | 陳舊 |
SLF4J(Simple Logging Facade for Java)★ | 適合 |
jboss-logging | 特殊專業領域使用 |
實現:
名稱 | 說明 |
---|---|
log4j★ | 最初版 |
JUL(java.util.logging) | JDK自帶 |
log4j2 | Apache收購log4j後全面重構,內部實現和log4j完全不同 |
logback★ | 優雅、強大 |
註:標記★的技術是同一作者。
[2]logback配置文件
<?xml version="1.0" encoding="UTF-8"?> <configuration debug="true"> <!-- 指定日志輸出的位置 --> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <!-- 日志輸出的格式 --> <!-- 按照順序分別是:時間、日志級別、線程名稱、打印日志的類、日志主體內容、換行 --> <pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern> </encoder> </appender> <!-- 設置全局日志級別。日志級別按順序分別是:DEBUG、INFO、WARN、ERROR --> <!-- 指定任何一個日志級別都隻打印當前級別和後面級別的日志。 --> <root level="DEBUG"> <!-- 指定打印日志的appender,這裡通過“STDOUT”引用瞭前面配置的appender --> <appender-ref ref="STDOUT" /> </root> <!-- 根據特殊需求指定局部日志級別 --> <logger name="com.atguigu.crowd.mapper" level="DEBUG"/> </configuration>
⑤ EHCache配置文件說明
當借助CacheManager.add(“緩存名稱”)創建Cache時,EhCache便會采用指定的的管理策略。
defaultCache標簽各屬性說明:
五、緩存基本原理
5.1 Cache接口
① 頂級接口
org.apache.ibatis.cache.Cache接口:所有緩存都必須實現的頂級接口
② Cache接口中的方法
③ 緩存的本質
根據Cache接口中方法的聲明我們能夠看到,緩存的本質是一個Map。
5.2 PerpetualCache
org.apache.ibatis.cache.impl.PerpetualCache是Mybatis的默認緩存,也是Cache接口的默認實現。Mybatis一級緩存和自帶的二級緩存都是通過PerpetualCache來操作緩存數據的。但是這就奇怪瞭,同樣是PerpetualCache這個類,怎麼能區分出來兩種不同級別的緩存呢?
其實很簡單,調用者不同。
一級緩存:由BaseExecutor調用PerpetualCache
二級緩存:由CachingExecutor調用PerpetualCache,而CachingExecutor可以看做是對BaseExecutor的裝飾
總結
到此這篇關於MyBatis緩存機制的文章就介紹到這瞭,更多相關MyBatis緩存機制內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!