MyBatis之緩存機制詳解

MyBatis之緩存機制詳解

    • 一、MyBatis緩存的基本概念
      • 1.1 緩存的核心價值
      • 1.2 MyBatis的兩級緩存體系
    • 二、一級緩存(SqlSession級別緩存)
      • 2.1 工作原理
      • 2.2 實戰案例:一級緩存演示
        • 2.2.1 基礎用法(默認開啟)
        • 2.2.2 一級緩存失效場景
      • 2.3 一級緩存的特點與適用場景
    • 三、二級緩存(Mapper級別緩存)
      • 3.1 工作原理
      • 3.2 二級緩存的開啟與配置
        • 3.2.1 全局配置(可選)
        • 3.2.2 Mapper接口開啟緩存
        • 3.2.3 實體類序列化(必須)
      • 3.3 實戰案例:二級緩存演示
      • 3.4 二級緩存的特點與適用場景
    • 四、二級緩存的高級配置
      • 4.1 禁用特定查詢的二級緩存
      • 4.2 強制刷新二級緩存
      • 4.3 整合第三方緩存(如Redis)
        • 4.3.1 引入依賴(Redis+MyBatis-Redis)
        • 4.3.2 配置Redis緩存
    • 五、緩存使用的常見問題與避坑指南
      • 5.1 一級緩存導致的臟讀問題
      • 5.2 二級緩存的序列化問題
      • 5.3 緩存與事務的一致性問題
      • 5.4 過度使用緩存導致內存溢出

緩存是提升數據庫查詢性能的關鍵技術,MyBatis內置了兩級緩存機制,能有效減少重復查詢的數據庫交互,降低數據庫壓力。

一、MyBatis緩存的基本概念

1.1 緩存的核心價值

數據庫查詢是應用性能的常見瓶頸(磁盤IO比內存IO慢10^6倍以上),緩存通過將頻繁查詢的結果存儲在內存中,避免重復訪問數據庫,從而:

  • 減少數據庫連接和SQL執行次數;
  • 降低數據庫服務器壓力;
  • 提升應用響應速度(從內存讀取比數據庫查詢快100倍以上)。

1.2 MyBatis的兩級緩存體系

MyBatis提供兩級緩存,工作流程如下:

  1. 一級緩存(SqlSession級別):默認開啟,緩存當前會話(SqlSession)的查詢結果;
  2. 二級緩存(Mapper級別):需手動開啟,緩存Mapper接口的查詢結果,可被多個SqlSession共享。

查詢數據時,MyBatis的緩存查詢順序:

二級緩存 → 一級緩存 → 數據庫

即先查二級緩存,若未命中則查一級緩存,仍未命中才查詢數據庫。

二、一級緩存(SqlSession級別緩存)

一級緩存是MyBatis的默認緩存,綁定到SqlSession(會話),生命周期與SqlSession一致。

2.1 工作原理

  • 緩存范圍:每個SqlSession擁有獨立的一級緩存,不同SqlSession的緩存互不影響;
  • 緩存時機SqlSession執行select查詢后,會將結果存入一級緩存;
  • 命中條件:相同的Mapper方法+相同的參數+相同的SQL
  • 失效場景SqlSession執行insert/update/delete(會清空當前SqlSession的一級緩存)、SqlSession關閉或提交。

2.2 實戰案例:一級緩存演示

2.2.1 基礎用法(默認開啟)
// 獲取SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);// 第一次查詢(未命中緩存,查詢數據庫)
User user1 = userMapper.selectById(1); // 第二次查詢(相同SqlSession+相同參數,命中一級緩存,不查數據庫)
User user2 = userMapper.selectById(1); System.out.println(user1 == user2); // true(同一對象,從緩存獲取)sqlSession.close(); // 關閉會話,一級緩存失效
2.2.2 一級緩存失效場景
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);User user1 = userMapper.selectById(1); // 執行更新操作(insert/update/delete),清空一級緩存
userMapper.updateAge(1, 25); 
sqlSession.commit(); // 提交事務(觸發緩存清空)// 再次查詢(緩存已清空,重新查詢數據庫)
User user2 = userMapper.selectById(1); 
System.out.println(user1 == user2); // false(不同對象,從數據庫獲取)

2.3 一級緩存的特點與適用場景

特點說明
默認開啟無需配置,開箱即用
會話隔離不同SqlSession的緩存獨立,避免數據沖突
自動管理增刪改自動清空緩存,保證數據一致性

適用場景

  • 單會話內的頻繁查詢(如同一請求中多次查詢相同用戶信息);
  • 讀多寫少的場景(避免頻繁查詢數據庫)。

三、二級緩存(Mapper級別緩存)

二級緩存是跨SqlSession的全局緩存,綁定到Mapper接口(同一Mapper的所有方法共享),需手動開啟。

3.1 工作原理

  • 緩存范圍:同一Mapper接口的所有SqlSession共享二級緩存;
  • 緩存時機SqlSession關閉(close())或提交(commit())后,一級緩存的結果會寫入二級緩存;
  • 命中條件:相同的Mapper接口+相同的方法+相同的參數
  • 失效場景:Mapper接口執行insert/update/delete(會清空當前Mapper的二級緩存)。

3.2 二級緩存的開啟與配置

3.2.1 全局配置(可選)

mybatis-config.xml中開啟二級緩存(默認已開啟,可省略):

<settings><setting name="cacheEnabled" value="true"/> <!-- 全局二級緩存開關 -->
</settings>
3.2.2 Mapper接口開啟緩存

在需要使用二級緩存的Mapper XML中添加<cache>標簽:

<!-- UserMapper.xml -->
<mapper namespace="com.example.mapper.UserMapper"><!-- 開啟二級緩存 --><cache eviction="LRU" <!-- 淘汰策略:LRU(最近最少使用) -->flushInterval="60000" <!-- 自動刷新間隔(毫秒,60秒) -->size="1024" <!-- 最大緩存條目 -->readOnly="false"/> <!-- 是否只讀(false:緩存對象副本) --><!-- 查詢語句(默認使用二級緩存) --><select id="selectById" resultType="User">SELECT id, username, age FROM user WHERE id = #{id}</select>
</mapper>

<cache>標簽屬性說明:

  • eviction:緩存淘汰策略(LRU:移除最近最少使用;FIFO:先進先出);
  • flushInterval:緩存自動刷新時間(毫秒,0表示不自動刷新);
  • size:最大緩存數量(過多會占用內存);
  • readOnlytrue(返回緩存對象本身,性能好但線程不安全);false(返回副本,安全但性能略低)。
3.2.3 實體類序列化(必須)

二級緩存可能將對象寫入磁盤(如使用第三方緩存),因此實體類需實現Serializable接口:

// 實現Serializable接口
public class User implements Serializable {private Integer id;private String username;private Integer age;// getter/setter
}

3.3 實戰案例:二級緩存演示

// 第一個SqlSession
SqlSession sqlSession1 = sqlSessionFactory.openSession();
UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
User user1 = userMapper1.selectById(1);
sqlSession1.close(); // 關閉會話,將一級緩存寫入二級緩存// 第二個SqlSession
SqlSession sqlSession2 = sqlSessionFactory.openSession();
UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
// 命中二級緩存(無需查詢數據庫)
User user2 = userMapper2.selectById(1); 
sqlSession2.close();System.out.println(user1 == user2); // false(二級緩存返回副本,readOnly=false時)
System.out.println(user1.getId().equals(user2.getId())); // true(數據一致)

3.4 二級緩存的特點與適用場景

特點說明
手動開啟需要在Mapper中配置<cache>標簽
跨會話共享同一Mapper的所有SqlSession可共享緩存
支持序列化可配置第三方緩存(如Redis)持久化緩存

適用場景

  • 多會話共享的高頻查詢(如商品分類、字典表等不常變化的數據);
  • 讀多寫少的場景(避免頻繁更新導致緩存失效)。

四、二級緩存的高級配置

4.1 禁用特定查詢的二級緩存

若某查詢不需要使用二級緩存(如實時性要求高的數據),可通過useCache="false"禁用:

<select id="selectLatestOrder" resultType="Order" useCache="false">SELECT * FROM `order` ORDER BY create_time DESC LIMIT 1
</select>

4.2 強制刷新二級緩存

若需在查詢時強制刷新緩存(忽略現有緩存,重新查詢數據庫并更新緩存),可使用flushCache="true"

<select id="selectUserWithForceRefresh" resultType="User" flushCache="true">SELECT * FROM user WHERE id = #{id}
</select>

4.3 整合第三方緩存(如Redis)

MyBatis的默認二級緩存是內存緩存(重啟后失效),生產環境通常整合Redis等分布式緩存,實現緩存持久化和分布式共享。

4.3.1 引入依賴(Redis+MyBatis-Redis)
<dependency><groupId>org.mybatis.caches</groupId><artifactId>mybatis-redis</artifactId><version>1.0.0-beta2</version>
</dependency>
<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>3.7.0</version>
</dependency>
4.3.2 配置Redis緩存
<!-- UserMapper.xml:指定緩存實現為Redis -->
<cache type="org.mybatis.caches.redis.RedisCache"><property name="host" value="localhost"/> <!-- Redis主機 --><property name="port" value="6379"/> <!-- Redis端口 --><property name="timeout" value="30000"/> <!-- 超時時間 --><property name="expiration" value="3600000"/> <!-- 緩存過期時間(毫秒) -->
</cache>

優勢

  • 緩存持久化(應用重啟后緩存不丟失);
  • 分布式共享(多實例應用共享緩存);
  • 支持緩存過期策略(自動清理過期數據)。

五、緩存使用的常見問題與避坑指南

5.1 一級緩存導致的臟讀問題

問題:多SqlSession場景下,一級緩存可能讀取到舊數據。

// SqlSession1查詢數據并緩存
SqlSession sqlSession1 = sqlSessionFactory.openSession();
User user1 = sqlSession1.getMapper(UserMapper.class).selectById(1);// SqlSession2更新數據并提交
SqlSession sqlSession2 = sqlSessionFactory.openSession();
sqlSession2.getMapper(UserMapper.class).updateAge(1, 26);
sqlSession2.commit();
sqlSession2.close();// SqlSession1再次查詢(一級緩存未更新,讀取到舊數據)
User user2 = sqlSession1.getMapper(UserMapper.class).selectById(1);
System.out.println(user2.getAge()); // 25(舊值,而非更新后的26)

解決方案

  • 避免長生命周期的SqlSession(如Web應用中,一個請求對應一個SqlSession);
  • 對實時性要求高的查詢,禁用一級緩存(通過flushCache="true");
  • 使用二級緩存(跨SqlSession共享,更新后會同步)。

5.2 二級緩存的序列化問題

錯誤:實體類未實現Serializable接口,二級緩存報錯NotSerializableException

解決方案

  • 確保所有存入二級緩存的實體類實現Serializable接口;
  • 若使用第三方緩存(如Redis),需保證對象可被序列化(如避免循環引用)。

5.3 緩存與事務的一致性問題

問題:事務未提交時,其他SqlSession可能讀取到未提交的緩存數據(臟讀)。

原因

  • 一級緩存在事務內生效,未提交的更新不會刷新其他SqlSession的緩存;
  • 二級緩存僅在事務提交后才更新,避免此問題。

解決方案

  • 優先使用二級緩存(事務提交后才寫入,保證數據一致性);
  • 關鍵業務(如支付)避免依賴緩存,直接查詢數據庫。

5.4 過度使用緩存導致內存溢出

問題:緩存大量數據(如全表查詢結果),導致JVM內存溢出(OOM)。

解決方案

  • 限制緩存大小(size屬性,如size="1024");
  • 設置緩存過期時間(flushInterval);
  • 避免緩存大集合(如分頁查詢,只緩存當前頁數據);
  • 使用分布式緩存(如Redis),利用外部內存存儲緩存。

總結:緩存機制的最佳實踐
MyBatis的兩級緩存各有適用場景,合理使用能顯著提升性能,核心實踐原則

  1. 一級緩存
  • 無需額外配置,充分利用其默認特性;
  • 注意SqlSession的生命周期(一個請求一個SqlSession),避免臟讀;
  • 增刪改操作后,一級緩存會自動清空,無需手動處理。
  1. 二級緩存
  • 僅對“讀多寫少、實時性要求低”的數據開啟(如字典表、商品分類);
  • 必須實現實體類序列化,避免緩存失敗;
  • 生產環境推薦整合Redis等分布式緩存,支持持久化和分布式共享;
  • 對實時性要求高的查詢(如訂單狀態),禁用二級緩存。
  1. 通用原則
  • 緩存粒度越小越好(優先緩存單條數據,而非全表);
  • 避免緩存大對象和頻繁變化的數據;
  • 結合監控工具(如MyBatis Log)分析緩存命中率,優化緩存策略。

若這篇內容幫到你,動動手指支持下!關注不迷路,干貨持續輸出!
ヾ(′? ˋ)ノヾ(′? ˋ)ノヾ(′? ˋ)ノヾ(′? ˋ)ノヾ(′? ˋ)ノ

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/89773.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/89773.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/89773.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

云服務器搭建自己的FRP服務。為什么客戶端的項目需要用Docker啟動,服務端才能夠訪問到?

簡單回答&#xff1a;在云服務器搭建FRP服務時&#xff0c;客戶端項目用Docker啟動并非必需&#xff0c;而是因為Docker的特性簡化了配置&#xff1a; Docker通過端口映射&#xff08;如-p 本地端口:容器端口&#xff09;能固定項目對外暴露的端口&#xff0c;減少本地端口沖突…

6 STM32單片機的智能家居安防系統設計(STM32代碼+手機APP設計+PCB設計+Proteus仿真)

系列文章目錄 文章目錄 系列文章目錄前言1 資料獲取與演示視頻1.1 資料介紹1.2 資料獲取1.3 演示視頻 2 系統框架3 硬件3.1 主控制器3.2 顯示屏3.3 WIFI模塊3.4 DHT11溫濕度傳感器3.5 煙霧/燃氣傳感器模塊&#xff1a;MQ-23.6 火焰傳感器3.7 門磁模塊MC-38 4 設計PCB4.1 安裝下…

DevOps落地的終極實踐:8大關鍵路徑揭秘!

本文來自騰訊藍鯨智云社區用戶: CanWay當前&#xff0c;DevOps因其能夠降低IT運營成本、提高軟件質量并加快上市時間的能力而在全球范圍內引起廣泛關注。它打破了傳統軟件開發與運營的界限&#xff0c;消除了新功能發布延遲和軟件質量下降的障礙。DevOps通過實施持續集成、持續…

react - 根據路由生成菜單

后端返回菜單的格式menuList:[{index: true,name: "",component: "../views/Home",meta: { title: "首頁", requiresAuth: true,roles:[user]},},{path: "/admin",name: "admin",meta: { title: "管理頁", roles:…

Window延遲更新10000天配置方案

1.點擊"開始"菜單&#xff0c;搜索"注冊表編輯器"&#xff0c;點擊"打開"。2.找到"\HKEY LOCAL MACHINE\SOFTWARE\Microsoft\WindowsUpdate\Ux\Settings"路徑。3.右面空白處右鍵新建一個32位值&#xff0c;命名為FlightSettingsMaxPau…

【OD機試】人民幣轉換

題目描述 將阿拉伯數字金額轉換為中文大寫金額格式,需遵循以下規則: 1、 前綴要求:中文大寫金額前必須標明“人民幣”字樣。 2、 用字規范:使用壹、貳、叁、肆、伍、陸、柒、捌、玖、拾、佰、仟、萬、億、元、角、分、零、整等字樣。 3、 “整”字規則: 金額到“元”為止…

在ajax中什么時候需要將返回值類型做轉換

$.ajax({url: TMSPROC0050/deleteData?accidentIds accidentIds.join(,),type: DELETE,dataType: json,success: function(result) {$(#accidentGrid).datagrid(reload);$.messager.show({title: 成功,msg: result.message})},error: function(result) {$.messager.alert({ti…

Helm常用命令大全(2025最新版)

文章目錄Helm常用命令大全&#xff08;2025最新版&#xff09;一、基礎命令與環境配置版本與幫助信息安裝與升級HelmLinux系統安裝版本升級注意事項二、倉庫管理命令倉庫基礎操作OCI倉庫支持&#xff08;v3.8新特性&#xff09;三、Chart操作命令Chart創建與打包Chart搜索與下載…

gitlab+jenkins

文章目錄架構gitlab和jenkins安裝jenkins配置gitlab配置jenkins與gitlab聯動參考架構 gitlab和jenkins安裝 部署docker 部署jenkins 啟動jenkins 用戶&#xff1a;admin&#xff0c;對應的密碼如下 點擊安裝自定義推薦的插件 安裝gitlab插件 jenkins配置 配置pipline…

Redis字符串操作指南:從入門到實戰應用

Redis作為一款高性能的鍵值存儲數據庫&#xff0c;其字符串&#xff08;String&#xff09;類型是最基礎也最常用的數據類型。它不僅能存儲簡單的文本信息&#xff0c;還能應對數字計算、二進制數據等多種場景&#xff0c;靈活且高效。接下來&#xff0c;我們就全方位剖析Redis…

SQLite 數據庫字段類型-詳細說明,數據類型詳細說明。

SQLite 數據類型 SQLite字段類型詳細說明&#xff0c;包含存儲類、親和類型、布爾類型、日期時間類型的存儲方式、取值范圍及核心特性。 創建 SQLite3 表時可使用的各種數據類型名稱&#xff0c;同時也介紹了相應的親和類型。 一、核心存儲類&#xff08;Storage Classes&am…

Node.js特訓專欄-實戰進階:17.會話管理與安全存儲

?? 歡迎來到 Node.js 實戰專欄!在這里,每一行代碼都是解鎖高性能應用的鑰匙,讓我們一起開啟 Node.js 的奇妙開發之旅! Node.js 特訓專欄主頁 專欄內容規劃詳情 會話管理與安全存儲:從原理到實戰的Web安全實踐 在Web應用中,會話(Session)是維持用戶狀態的核心機制—…

【橘子分布式】gRPC(編程篇-中)

一、簡介 我們之前已經完成了對于api模塊的開發&#xff0c;也就是已經生成了基礎的類和對應的接口&#xff0c;現在我們需要完成的是client和server端的開發。其實如同thrift一樣&#xff0c;現在要做的就是實現我們之前定義的service里面的hello方法&#xff0c;里面寫我們的…

Spring Boot 項目中數據同步之binlog和MQ

在 Spring Boot 項目中&#xff0c;“監聽 binlog” 和 “業務代碼中集成 MQ” 是實現數據同步、事件驅動的兩種主流方法。 簡單來說&#xff0c;這個選擇可以概括為&#xff1a; 監聽 Binlog (如使用 Canal)&#xff1a;像一個數據庫的貼身秘書&#xff0c;它忠實地記錄數據庫…

MySQL 寫入性能優化全攻略(附 GitHub 面試題項目鏈接)

面試中你可能會遇到這樣的問題&#xff1a; &#x1f4ac; “假設你的接口一天收到百萬級請求&#xff0c;MySQL 撐得住嗎&#xff1f;你會怎么優化寫入性能&#xff1f;” 剛開始我也懵過&#xff0c;后來不斷復盤與總結&#xff0c;現在我可以用結構化方式給出一個相對完整的…

用Dynamic chunk去干掉tokenizer?

一般你們下AR模型的時候&#xff0c;都有這個&#xff0c;也就是tokenzier&#xff0c;tokenizer是干啥的&#xff0c;其實就是你的分詞字典不光有specal的token對應的還有實際的對應的分詞對應的代碼&#xff0c;比如&#xff1a;也有tokenzier沒顯示的&#xff0c;比如&#…

Linux系統日志管理入門:journalctl命令完全指南

Linux系統日志管理入門&#xff1a;journalctl命令完全指南前言一、journalctl介紹二、基礎使用&#xff1a;快速上手1. 查看全部日志2. 查看本次啟動的日志3. 按時間篩選日志4. 按服務&#xff08;單元&#xff09;過濾日志三、常用參數與場景四、實戰案例&#xff1a;解決實際…

神經網絡的基本骨架——nn.Module的使用(torch.nn庫)

在 PyTorch 中&#xff0c;nn.Module 是所有神經網絡模塊的基類&#xff0c;用于構建和組織深度學習模型。它提供了一系列工具和功能&#xff0c;使模型的定義、訓練和部署更加高效和靈活。nn Neural Network&#xff08;神經網絡&#xff09;核心作用&#xff1a;模塊化設計&…

靜態住宅IP和節點有什么區別?哪種更適合你的需求?

在跨境電商、社媒運營等業務中&#xff0c;“靜態住宅IP”和“節點”常被混淆使用&#xff0c;但兩者代表網絡架構中不同層級的資源。選錯可能導致賬號風控、業務效率低下。IPdodo將在本篇文章中&#xff0c;從本質、業務場景到選擇策略&#xff0c;為您一文道清兩者之間的區別…

AI編程工具對比:Cursor、GitHub Copilot與Claude Code

文章目錄AI編程工具對比&#xff1a;Cursor、GitHub Copilot與Claude Code一、產品定位與核心架構1.1 Cursor&#xff1a;AI原生IDE的代表1.2 GitHub Copilot&#xff1a;代碼補全的行業標桿1.3 Claude Code&#xff1a;終端Agent的革新者二、核心功能深度對比2.1 代碼生成與理…