帶你深入理解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!

推薦閱讀: