如何避免MyBatis二級緩存中的臟讀

避免 MyBatis 二級緩存中的臟讀問題(即緩存數據與數據庫實際數據不一致),需要從緩存更新機制、配置策略、業務設計等多維度入手。以下是經過實踐驗證的解決方案,結合底層原理和具體實現:

一、理解二級緩存臟讀的根源

臟讀的本質是緩存數據未及時同步數據庫更新。二級緩存是 Mapper 級別的共享緩存,當某一 SqlSession 更新數據后,若其他 SqlSession 仍使用舊緩存,就會導致臟讀。常見觸發場景:

  • 更新操作未觸發緩存清空
  • 事務未正常提交導致緩存未刷新
  • 跨 Mapper 操作導致緩存同步失效
  • 分布式環境下緩存未全局同步

二、解決方案詳解

1. 依賴 MyBatis 自動緩存清空機制

MyBatis 默認在執行insert/update/delete操作時,會自動清空當前 Mapper 的二級緩存(通過flushCache="true"實現)。需確保該機制正常生效:

核心原理
更新操作會觸發緩存清空,保證后續查詢能從數據庫獲取最新數據。但需注意:只有事務提交后,緩存清空才會生效

實現示例

<!-- Mapper.xml中默認配置(無需手動添加,但需確認) -->
<update id="updateUser" flushCache="true">UPDATE t_user SET username = #{username} WHERE id = #{id}
</update><insert id="insertUser" flushCache="true">INSERT INTO t_user (username, email) VALUES (#{username}, #{email})
</insert>

注意

  • 不要手動將flushCache設為false,這會禁用自動清空,直接導致臟讀。
  • 若使用注解方式,需確保@Update/@Insert/@Delete注解的方法默認觸發緩存清空(MyBatis 注解默認行為與 XML 一致)。
2. 控制查詢語句的緩存刷新策略

對于實時性要求極高的查詢(如庫存、余額),可強制每次查詢都刷新緩存,避免使用舊數據:

實現方式
select標簽中設置flushCache="true",每次查詢前清空緩存:

<select id="selectUserById" resultType="User" flushCache="true">SELECT id, username, email FROM t_user WHERE id = #{id}
</select>

適用場景

  • 高頻更新且實時性要求高的數據(如訂單狀態、庫存數量)。
  • 避免:全局使用該配置,會導致緩存失效,失去性能優化意義。
3. 精細化控制緩存粒度

二級緩存默認以 Mapper 為單位(namespace 級別),粒度較粗。若同一 Mapper 中包含多表操作,可能導致無關更新觸發緩存清空,或相關更新未觸發清空。

優化方案

  • 拆分 Mapper:按表或業務模塊拆分 Mapper,確保緩存粒度與數據更新范圍匹配。
    例:UserMapper只處理t_user表,OrderMapper只處理t_order表,避免跨表操作導致緩存混亂。

  • 使用cache-ref共享緩存:若多表存在強關聯(如useruser_profile),可通過cache-ref讓多個 Mapper 共享同一緩存,確保更新任一表時同步清空關聯緩存:

    <!-- UserMapper.xml -->
    <cache eviction="LRU" flushInterval="30000"/><!-- UserProfileMapper.xml 共享UserMapper的緩存 -->
    <cache-ref namespace="com.example.mapper.UserMapper"/>
    

    此時,更新user_profile表會清空UserMapper的緩存,避免關聯數據臟讀。

4. 嚴格控制事務邊界

在 Spring+MyBatis 環境中,事務未提交會導致緩存更新延遲,是臟讀的常見誘因。

原理
SqlSession 在事務提交前,更新操作的緩存清空不會生效(二級緩存寫入 / 清空操作在事務提交后執行)。若事務未正常提交(如異常回滾),緩存不會更新,導致后續查詢仍使用舊數據。

解決方案

  • 確保更新操作在事務中執行,并正常提交。
  • 避免長事務持有 SqlSession,減少緩存不一致窗口。

代碼示例

@Slf4j
@Service
public class UserService {@Autowiredprivate UserMapper userMapper;/*** 正確的事務管理:更新后提交事務,觸發緩存清空*/@Transactionalpublic void updateUser(Long id, String newUsername) {User user = userMapper.selectById(id);if (Objects.isNull(user)) {log.warn("用戶不存在,id: {}", id);return;}user.setUsername(newUsername);userMapper.update(user);// 事務提交后,MyBatis會自動清空UserMapper的二級緩存}
}
5. 配置合理的緩存過期時間

即使緩存更新機制失效,合理的過期時間也能減少臟讀影響。通過flushInterval設置自動刷新間隔:

<cache eviction="LRU" flushInterval="60000"  <!-- 60秒自動刷新一次緩存 -->size="1024" readOnly="false"/>

適用場景

  • 非核心數據(如商品分類、地區信息),允許短時間不一致。
  • 作為兜底機制,避免緩存永久臟數據。
6. 禁用敏感數據的二級緩存

對于強一致性要求的數據(如用戶余額、訂單狀態),直接禁用二級緩存,優先保證數據準確性:

實現方式

  • 全局禁用:在mybatis-config.xml中關閉二級緩存(不推薦,會影響所有 Mapper):

    xml

    <settings><setting name="cacheEnabled" value="false"/>
    </settings>
    
  • 局部禁用:在特定select標簽中禁用:

    xml

    <select id="selectUserBalance" resultType="BigDecimal" useCache="false">SELECT balance FROM t_user_balance WHERE user_id = #{userId}
    </select>
    
7. 分布式環境下使用集中式緩存

單機環境下,二級緩存使用內存存儲;分布式環境下,多節點的本地緩存無法同步,必然導致臟讀。

解決方案
集成 Redis、Memcached 等分布式緩存,確保所有節點共享同一緩存源:

  1. 引入 MyBatis-Redis 依賴:
<dependency><groupId>org.mybatis.caches</groupId><artifactId>mybatis-redis</artifactId><version>1.0.0-beta2</version>
</dependency>
  1. 配置 Redis 緩存(redis.properties):

properties

redis.host=127.0.0.1
redis.port=6379
redis.timeout=2000
redis.default.expiration=300000  # 5分鐘過期
  1. 在 Mapper 中指定 Redis 緩存:
<mapper namespace="com.example.mapper.UserMapper"><cache type="org.mybatis.caches.redis.RedisCache"/><!-- SQL語句 -->
</mapper>

優勢

  • 分布式環境下緩存全局一致,避免節點間數據差異。
  • 支持緩存過期、集群同步等高級特性,進一步減少臟讀風險。
8. 手動管理緩存(極端場景)

對于復雜業務(如跨服務更新),可通過 MyBatis 的Cache接口手動操作緩存:

@Slf4j
@Service
public class CacheManagerService {@Autowiredprivate SqlSessionFactory sqlSessionFactory;/*** 手動清空指定Mapper的二級緩存*/public void clearMapperCache(String mapperNamespace) {Configuration configuration = sqlSessionFactory.getConfiguration();Cache cache = configuration.getCache(mapperNamespace);if (Objects.nonNull(cache)) {cache.clear();log.info("已手動清空緩存,namespace: {}", mapperNamespace);}}
}

適用場景

  • 跨微服務更新數據后,手動觸發緩存清空。
  • 定時任務刷新緩存(如凌晨批量更新后全量清空)。

三、總結:避免臟讀的核心原則

  1. 優先依賴自動機制:信任 MyBatis 的flushCache默認行為,不隨意修改配置。
  2. 事務是基礎:確保更新操作在事務中執行并正常提交。
  3. 粒度要匹配:緩存范圍(Mapper)與數據更新范圍保持一致。
  4. 按需禁用:強一致性數據直接禁用二級緩存,不冒風險。
  5. 分布式必用集中緩存:單機緩存無法滿足分布式環境的一致性要求。

通過以上措施,可從根本上避免二級緩存的臟讀問題,在性能優化與數據一致性之間找到平衡。

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

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

相關文章

Python實現RANSAC進行點云直線、平面、曲面、圓、球體和圓柱擬合

本節我們分享使用RANSAC算法進行點云的擬合。RANSAC算法是什么&#xff1f;不知道的同學們前排罰站&#xff01;(前面有)總的來說&#xff0c;RANSAC&#xff08;Random Sample Consensus&#xff09;是一種通用的迭代魯棒估計框架&#xff0c;無論擬合何種幾何模型&#xff0c…

實驗2 天氣預報

實驗1 天氣預報一、實驗目標二、實驗步驟&#xff08;一&#xff09;準備工作&#xff08;二&#xff09;小程序開發項目創建頁面配置視圖設計邏輯實現三、程序運行結果四、問題總結與體會主要問題及解決方案主要收獲chunk的博客地址一、實驗目標 1、掌握服務器域名配置和臨時…

【CVE-2025-5419】(內附EXP) Google Chrome 越界讀寫漏洞【內附EXP】

前言 近日,奇安信CERT監測到Google Chrome中曝出一枚高危安全漏洞(CVE-2025-5419,QVD-2025-21836),該漏洞屬于越界讀寫問題,攻擊者只需通過構造惡意網頁,就可能觸發漏洞,從而繞過Chrome的沙箱防護,直接實現遠程代碼執行,最終完全控制用戶設備。目前,安全社區已確認…

【科研繪圖系列】R語言在海洋生態學中的應用:浮游植物糖類組成與溶解性有機碳的關系

禁止商業或二改轉載,僅供自學使用,侵權必究,如需截取部分內容請后臺聯系作者! 文章目錄 介紹 數據準備 數據處理 糖類組成隨年齡的變化 糖類組成與DOC含量的關系 數據可視化 加載R包 數據下載 導入數據 數據預處理 畫圖 總結 系統信息 介紹 本教材通過R語言及其強大的數據…

webpack文件指紋:hash、chunkhash與contenthash詳解

文件指紋就是打包后輸出文件的后綴&#xff0c;每次構建都會生成不同的文件后綴&#xff0c;這樣可以防止瀏覽器的默認緩存&#xff0c;使客戶端代碼可以及時修改。文件指紋的三種方式&#xff1a;? hash ?&#xff1a;基于整個項目構建內容生成全局哈希值&#xff0c;任何文…

Pytest 插件怎么寫:從0開發一個你自己的插件

概述 你用過 pytest-html 生成報告,或用 pytest-xdist 并行運行測試嗎?這些強大的功能,其實都是 Pytest 插件 這些都是我們引入項目后直接使用的,當然 你也可以自己寫一個 Pytest 插件 基本原理 Pytest 的強大,源于它的 插件系統。它允許你通過定義特定的函數(稱為 H…

Java:IO流——基礎篇

目錄 前言 一、File 類 1、概述 ①構造方法 ②實例對象 2、使用 ①查看名稱、路徑、長度 ②判斷、創建和刪除操作 ③目錄遍歷操作 二、IO流 1、流的概念 2、流的分類 ①按數據流向 ②按數據類型 ③按功能 3、字節流 ⑴FileInputStream——文件輸入流 ⑵FileOutputStream——文件…

數據挖掘 5.1~5.2 PCA——前言

5.1 Twelve ways to fool the masses 5.1 愚弄大眾的十二種方法 5.2.1 Prelim: Old MacDonald meets Lagrange 5.2.1 前言&#xff1a;老麥克唐納遇見拉格朗日 5.2. Prelim: Meet stubborn vectors 5.2. 前言&#xff1a;遇見頑固向量 5.2.3 Prelim: Covariance and its friend…

DeepSeek分析

(非走向數字時代,融入數字生活,構建數字生態的分解,只是感覺可以分享給大家---因此現設置VIP,旺海涵) 這是deepseek剛爆的時候,春節緊急對其做的分析。 內容還是私藏狀態,做了初步評估,感覺可以分享給大家!!! 但是非共享的構建數字生態的核心,因此添加了vip設置…

2025第五屆人工智能、自動化與高性能計算國際會議 (AIAHPC 2025)

重要信息 官網&#xff1a;www.aiahpc.org 時間&#xff1a;2025年9月19-21日 地點&#xff1a;中國合肥 主題 1、高性能計算 并行和分布式系統架構 高性能計算的語言和編譯器 并行和分布式軟件技術 并行和分布式算法 嵌入式系統 計算智能 點對點計算 網格和集群計算…

CORS解決跨域問題的多個方案 - nginx站點配置 / thinkphp框架內置中間件 / 純前端vue、vite的server.proxy代理

效果圖 跨域報錯 跨域解決 方案實測 1. nginx、apache站點配置 > OK 2. thinkphp框架內置中間件 “跨域請求支持” > OK 3. 純前端vue、vite的server.proxy代理 > 不OK 方案具體設置 1. nginx、apache站點配置 > OK 修改nginx服務器的站點的跨域信息 日志下…

什么是Omni-Hub?一套面向“萬物智聯”時代的操作系統級方法論

Omni-Hub&#xff08;中文常譯“全向中樞”&#xff09;&#xff0c;是一套面向未來數字化生態的開放型系統級框架&#xff0c;由“Omni”&#xff08;全域、全向、全模態&#xff09;與“Hub”&#xff08;中樞、樞紐&#xff09;組合而成&#xff0c;旨在通過統一接口、協議與…

ARP地址解析協議

工作原理ARP是一個封裝于數據鏈路層的二層協議&#xff0c;其目的主要是將IP地址解析為MAC地址&#xff0c;通過廣播&#x1f509;詢問Who is x.x.x.x&#xff0c;對方收到后單播回應自己的mac地址動態ARP動態ARP通過ARP協議自動學習和維護IP與MAC的映射關系&#xff0c;表項具…

PortSwigger靶場之Blind SQL injection with out-of-band interaction通關秘籍

一、題目分析 該實驗室存在一個盲 SQL 注入漏洞。該應用程序使用跟蹤 cookie 進行分析&#xff0c;并執行包含所提交 cookie 值的 SQL 查詢。該 SQL 查詢是異步執行的&#xff0c;不會對應用程序的響應產生影響。不過&#xff0c;我們可以與外部域觸發非帶內交互。要解決此漏洞…

筆試-筆記3

1.在以下聲明中哪一個表示“指向常量的指針”(指針指向的內容不能修改)&#xff1f; A.char* const p B.const char* p C.char *p const D.char const p 解析&#xff1a; 選B&#xff0c;const修飾的變量為常量&#xff0c;意味著不能修改 A是常量指針&#xff0c;const修飾的…

Linux正則表達式

文章目錄一、Linux正則表達式與三劍客知識1.什么是正則表達式&#xff1f;2.為什么要學習正則表達式&#xff1f;3.有關正則表達式容易混淆的事項4.學習正則表達式注意事項5. 正則表達式的分類5.1 基本的正則表達式&#xff08;BRE&#xff09;集合6. 正則表達式測試題7. 擴展正…

MATLAB Figure畫布中繪制表格詳解

文章目錄 1 使用uitable創建帶有樣式和顏色映射的表格 2 使用imagesc和text創建自定義表格 3 使用patch和text創建完全自定義的表格 4 代碼詳細講解 4.1 使用uitable 4.2 使用imagesc和text 4.3 使用patch和text 5 顏色映射技巧 5.1 使用內置顏色映射 5.2 自定義顏色映射函數 5…

Python在語料庫建設中的應用:文本收集、數據清理與文件名管理

一、問題的提出在日常語言學習與教學中&#xff0c;語料庫是一個不可或缺的工具。它可以幫助我們查找高頻詞&#xff0c;獲取搭配信息、例句信息、關鍵詞信息等。由于建庫過程操作步驟多&#xff0c;有時還要用到圖片識別、格式轉化、文本清理等技巧&#xff0c;很多人往往都止…

STL——priority_queue的使用(快速入門詳細)

目錄 前言 一、基本知識 二、使用 前言 priority_queue是在queue庫里的&#xff0c;所以使用的時候要包含queue頭文件。使用方法和堆類似&#xff0c;因為它的底層其實就是大根堆。 一、基本知識 優先隊列優先級隊列是一種容器適配器&#xff0c;根據一些嚴格的弱排序標準&…

MATLAB中函數的詳細使用

一、函數基本知識function語法&#xff1a; function [,...,] myfun(,...,)&#xff0c; …