如何避免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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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; …