Mybatis中的一级二级缓存扫盲

 思维导图:

 

  MyBatis 提供了一级缓存和二级缓存机制,用于提高数据库查询的性能,减少对数据库的访问次数。(本质上是减少IO次数)。


一级缓存


1. 概念

  一级缓存也称为会话缓存,它是基于 SqlSession 的缓存。在同一个 SqlSession 中,执行相同的 SQL 查询时,MyBatis 会优先从一级缓存中获取结果,而不是再次访问数据库。

2. 工作原理

2.1缓存结构:

  在 SqlSession 内部,一级缓存是一个 PerpetualCache 对象,它本质上是一个 HashMap,键是根据查询的 SQL 语句、参数、环境等信息生成的唯一标识,值是查询结果。

  查询流程:当调用 SqlSession 的查询方法时,MyBatis 会先将查询的 SQL 语句、参数等信息组合成一个唯一的缓存键。然后在 PerpetualCache 这个 HashMap 中查找该键对应的值。如果找到了,就直接返回该值;如果没找到,就会执行 SQL 查询,将查询结果存入 PerpetualCache 中,下次再执行相同查询时就可以直接从缓存中获取结果。

2.2 缓存命中的条件

相同的 SqlSession:必须是在同一个 SqlSession 实例中执行相同的查询,一级缓存才会生效。
相同的 SQL 语句:查询的 SQL 语句必须完全相同,包括 SQL 中的参数占位符和参数值。
相同的环境:查询的环境(如数据库连接、事务等)也必须相同。

代码示例:

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.InputStream;public class FirstLevelCacheDetailExample {public static void main(String[] args) throws Exception {String resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);// 创建第一个 SqlSessiontry (SqlSession sqlSession1 = sqlSessionFactory.openSession()) {// 第一次查询User user1 = sqlSession1.selectOne("com.example.UserMapper.selectUserById", 1);System.out.println("第一次查询结果: " + user1);// 第二次查询,使用相同的 SqlSessionUser user2 = sqlSession1.selectOne("com.example.UserMapper.selectUserById", 1);System.out.println("第二次查询结果: " + user2);// 两次查询结果相同,说明使用了一级缓存System.out.println("两次查询结果是否相同: " + (user1 == user2));// 执行更新操作sqlSession1.update("com.example.UserMapper.updateUser", new User(1, "New Name"));// 第三次查询User user3 = sqlSession1.selectOne("com.example.UserMapper.selectUserById", 1);System.out.println("第三次查询结果: " + user3);// 由于执行了更新操作,一级缓存已清空,user3 是重新查询数据库得到的结果System.out.println("第一次查询结果和第三次查询结果是否相同: " + (user1 == user3));}}
}

  在上述代码中,前两次查询使用相同的 SqlSession 和相同的查询条件,所以第二次查询会从一级缓存中获取结果。而执行更新操作后,一级缓存被清空,第三次查询会重新访问数据库。

3. 缓存失效情况

  • 不同的 SqlSession:每个 SqlSession 都有自己独立的一级缓存,不同的 SqlSession 之间的缓存是不共享的。
  • 执行 insert、update、delete 操作:当在同一个 SqlSession 中执行这些操作时,会清空该 SqlSession 的一级缓存,以保证数据的一致

4. 优缺点

优点:

  • 提高性能:在同一个 SqlSession 中多次执行相同查询时,避免了重复的数据库访问,减少了数据库的负载,提高了查询性能。
  • 简单易用:一级缓存是 MyBatis 内置的,无需额外配置,默认开启,使用方便。

缺点:

  • 作用范围小:只在同一个 SqlSession 中有效,不同的 SqlSession 之间无法共享缓存,限制了缓存的使用范围。
  • 数据一致性问题:如果在同一个 SqlSession 中执行了 insert、update、delete 操作,会清空该 SqlSession 的一级缓存,但如果在不同的 SqlSession 中对同一数据进行了修改,一级缓存可能会返回旧数据,导致数据不一致。

二级缓存

1. 工作原理

缓存结构:

二级缓存也是基于 PerpetualCache 实现的,但它是基于 SqlSessionFactory 的。每个 Mapper 可以有自己独立的二级缓存,也可以多个 Mapper 共享同一个二级缓存。

查询流程:当一个 SqlSession 执行查询操作时,MyBatis 会先检查该 Mapper 对应的二级缓存中是否存在该查询结果。如果存在,则直接从二级缓存中获取结果;如果不存在,则执行 SQL 查询,并将查询结果存入二级缓存中。在多个 SqlSession 之间,只要它们是由同一个 SqlSessionFactory 创建的,就可以共享二级缓存。

2. 配置详解

全局配置:在 mybatis-config.xml 中开启二级缓存的全局开关。

<settings><setting name="cacheEnabled" value="true"/>
</settings>

 Mapper 配置:在 Mapper 映射文件中配置缓存。

<mapper namespace="com.example.UserMapper"><!-- 开启二级缓存,并配置相关属性 --><cacheeviction="LRU" <!-- 缓存淘汰策略,这里使用最近最少使用策略 -->flushInterval="60000" <!-- 缓存刷新间隔,单位为毫秒 -->size="512" <!-- 缓存的最大对象数 -->readOnly="true" /> <!-- 是否只读 --><select id="selectUserById" resultType="com.example.User">SELECT * FROM users WHERE id = #{id}</select>
</mapper>

  • eviction:缓存淘汰策略,常见的有 LRU(最近最少使用)、FIFO(先进先出)等。
  • flushInterval:缓存刷新间隔,指定多长时间清空一次缓存。
  • size:缓存的最大对象数,当缓存中的对象数量超过该值时,会根据淘汰策略淘汰部分对象。
  • readOnly:是否只读。如果设置为 true,则缓存中的对象是只读的,MyBatis 会直接返回缓存中的对象,不会进行序列化和反序列化操作,性能较高;如果设置为 false,则每次返回缓存中的对象时都会进行序列化和反序列化操作,保证返回的对象是一个新的实例,但性能相对较低。

3. 示例代码及分析

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.InputStream;public class SecondLevelCacheDetailExample {public static void main(String[] args) throws Exception {String resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);// 第一个 SqlSessiontry (SqlSession sqlSession1 = sqlSessionFactory.openSession()) {User user1 = sqlSession1.selectOne("com.example.UserMapper.selectUserById", 1);System.out.println("第一个 SqlSession 查询结果: " + user1);}// 第二个 SqlSessiontry (SqlSession sqlSession2 = sqlSessionFactory.openSession()) {User user2 = sqlSession2.selectOne("com.example.UserMapper.selectUserById", 1);System.out.println("第二个 SqlSession 查询结果: " + user2);// 两次查询结果相同,说明使用了二级缓存System.out.println("两次查询结果是否相同: " + (user1 == user2));}// 执行更新操作try (SqlSession sqlSession3 = sqlSessionFactory.openSession()) {sqlSession3.update("com.example.UserMapper.updateUser", new User(1, "New Name"));sqlSession3.commit(); // 提交事务,清空二级缓存}// 第三个 SqlSessiontry (SqlSession sqlSession4 = sqlSessionFactory.openSession()) {User user3 = sqlSession4.selectOne("com.example.UserMapper.selectUserById", 1);System.out.println("第三个 SqlSession 查询结果: " + user3);// 由于执行了更新操作,二级缓存已清空,user3 是重新查询数据库得到的结果System.out.println("第一个查询结果和第三个查询结果是否相同: " + (user1 == user3));}}
}

 

  在上述代码中,第一个 SqlSession 执行查询后,结果会存入二级缓存。第二个 SqlSession 执行相同查询时,会从二级缓存中获取结果。执行更新操作并提交事务后,二级缓存会被清空,第三个 SqlSession 执行查询时会重新访问数据库。

3. 缓存失效情况

  • 执行 insert、update、delete 操作:当执行这些操作时,会清空该 Mapper 对应的二级缓存,以保证数据的一致性。
  • 缓存刷新策略:可以通过配置缓存的刷新策略,如 flushInterval 来定期清空缓存。

4. 优缺点

优点

  • 作用范围大:多个 SqlSession 可以共享二级缓存,减少了数据库的访问次数,提高了系统的整体性能。
  • 可配置性强:可以通过配置不同的缓存淘汰策略、刷新间隔等参数,满足不同的业务需求。

缺点

  • 配置复杂:需要在全局配置和 Mapper 映射文件中进行配置,相对一级缓存来说配置较为复杂。
  • 数据一致性问题:如果在不同的 Mapper 中对同一数据进行了修改,可能会导致二级缓存中的数据不一致,需要手动清空缓存或使用更复杂的缓存刷新策略。

5.一级缓存和二级缓存的比较

作用范围:一级缓存是基于 SqlSession 的,作用范围较小;二级缓存是基于 SqlSessionFactory 的,作用范围较大。

缓存共享:一级缓存不共享,每个 SqlSession 有自己独立的缓存;二级缓存可以在多个 SqlSession 之间共享。

开启方式:一级缓存默认开启;二级缓存需要手动配置开启。
通过合理使用一级缓存和二级缓存,可以有效提高 MyBatis 应用的性能。但在使用缓存时,需要注意数据的一致性问题,避免出现脏数据。

总结

  MyBatis 的一级缓存和二级缓存各有优缺点,在实际应用中需要根据具体的业务场景合理使用。一级缓存适用于在同一个 SqlSession 中多次执行相同查询的场景,而二级缓存适用于多个 SqlSession 之间共享缓存的场景。同时,需要注意缓存的使用可能会导致数据一致性问题,需要在业务逻辑中进行相应的处理。

    本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/78157.shtml

    如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

    相关文章

    uniapp 实现低功耗蓝牙连接并读写数据实战指南

    在物联网应用场景中&#xff0c;低功耗蓝牙&#xff08;BLE&#xff09;凭借其低能耗、连接便捷的特点&#xff0c;成为设备间数据交互的重要方式。Uniapp 作为一款跨平台开发框架&#xff0c;提供了丰富的 API 支持&#xff0c;使得在多个端实现低功耗蓝牙功能变得轻松高效。本…

    OpenSSL应用实践:嵌入式数据安全实战指南

    文章目录 OpenSSL应用实践:嵌入式数据安全实战指南一、嵌入式安全现状与OpenSSL适配方案1.1 嵌入式安全挑战1.2 OpenSSL精简方案二、开发环境搭建2.1 交叉编译工具链2.2 OpenSSL交叉编译三、核心功能实现3.1 AES-GCM加密实践四、实战项目:安全OTA升级4.1 系统架构4.2 关键代码…

    harmonyOS 手机,双折叠,平板,PC端屏幕适配

    由于HarmonyOS设备的屏幕尺寸和分辨率各不相同&#xff0c;开发者需要采取适当的措施来适配不同的屏幕。 1.EntryAbility.ets文件里&#xff1a;onWindowStageCreate方法里判断设备类型&#xff0c; 如果是pad&#xff0c;需全屏展示&#xff08;按客户需求来&#xff0c;本次…

    跟韩学AiOps系列之2025学MySQL系列_如何在MySQL中开启和提交事务?!

    跟韩学AiOps系列之2025学MySQL系列_如何在MySQL中开启和提交事务&#xff1f;! 文章目录 一、事务的基本操作1. 开启事务2. 执行事务内操作3. 提交事务4. 回滚事务 二、验证示例&#xff08;适用于 MySQL 5.7&#xff09;步骤 1&#xff1a;准备测试表和数据步骤 2&#xff1a…

    Java生成微信小程序码及小程序短链接

    使用wx-java-miniapp-spring-boot-starter 生成微信小程序码及小程序短链接 在pom.xml文件中引入依赖 <dependency><groupId>com.github.binarywang</groupId><artifactId>wx-java-miniapp-spring-boot-starter</artifactId><version>4.7…

    如何让通义千问大模型支持结构化输出?

    之前的文章提到通义千问API无法通过with_structured_output/json schema的方式支持结构化输出&#xff0c;如果就是想使用通义千问大模型做结构化输出&#xff0c;应该怎么办呢&#xff1f;有两种办法 使用Ollama来运行通义千问大模型 从Ollama博客文章 Structured output 中…

    一条 SQL 查询语句是如何执行的(MySQL)

    第一讲&#xff1a;一条 SQL 查询语句是如何执行的 总览图示 MySQL 查询的执行流程可以大致分为以下步骤&#xff08;如图所示&#xff09;&#xff1a; 连接器&#xff08;Connection&#xff09;查询缓存&#xff08;Query Cache&#xff0c;MySQL 8.0 已废弃&#xff09;…

    汽车OTA在线升级法规分析

    摘要 本文介绍了R156法规即《关于批准车辆的软件升级和软件升级管理体系统一规定的法规》、该法规专注于汽车软件升级功能&#xff0c;并为此提出了一系列具体要求&#xff0c;旨在确保软件升级流程的安全性、可控性和合规性&#xff0c;从而顺应汽车行业智能化、联网化的发展趋…

    Notepad编辑器实现换行符替换

    在不同的Note编辑器中&#xff0c;批量把换行替换为空的方法有所不同&#xff0c;以下是常见编辑器的操作方法&#xff1a; Notepad 打开文件后&#xff0c;按CtrlH打开“查找和替换”对话框&#xff0c;在“查找”字段中输入\r\n&#xff0c;在“替换为”字段中输入一个空格…

    Rust多线程性能优化:打破Arc+锁的瓶颈,效率提升10倍

    一、引言 在 Rust 开发中&#xff0c;多线程编程是提升程序性能的重要手段。Arc&#xff08;原子引用计数&#xff09;和锁的组合是实现多线程数据共享的常见方式。然而&#xff0c;很多程序员在使用 Arc 和锁时会遇到性能瓶颈&#xff0c;导致程序运行效率低下。本文将深入剖…

    【安装指南】Centos7 在 Docker 上安装 RabbitMQ4.0.x

    目录 前置知识:RabbitMQ 的介绍 一、单机安装 RabbitMQ 4.0.7版本 1.1 在线拉取镜像 二、延迟插件的安装 2.1 安装延迟插件 步骤一:下载延迟插件 步骤二:将延迟插件放到插件目录 步骤三:启动延迟插件 步骤四:重启 RabbitMQ 服务 步骤五:验收成果 步骤六:手动…

    【quantity】5 derive_more库 2.0 版介绍

    derive_more 是一个 Rust 过程宏库&#xff0c;旨在通过派生宏自动生成常见 trait 的实现&#xff0c;减少样板代码。2.0 版本带来了多项改进和新特性。 主要特性 1. 支持的 Trait 派生 derive_more 2.0 支持派生以下 trait&#xff1a; 基本操作 trait: Display - 格式化显…

    网站备份,网站数据备份的步骤

    网站备份&#xff08;尤其是网站数据备份&#xff09;是保障业务连续性、防止数据丢失和应对安全威胁的关键措施。以下是系统化的备份步骤和实施建议&#xff0c;涵盖技术操作、策略规划及常见问题处理&#xff1a; 一、备份前的准备工作 明确备份范围 核心数据&#xff1a;…

    OpenCV 图形API(72)图像与通道拼接函数-----根据指定的方式翻转图像(GMat)函数 flip()

    操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 翻转一个2D矩阵&#xff0c;围绕垂直轴、水平轴或同时围绕两个轴。 该函数以三种不同的方式之一翻转矩阵&#xff08;行和列的索引是从0开始的&a…

    医生视角下转录组学的生物信息学分析

    医生视角下转录组学的生物信息学分析 转录组学的生物信息学分析是医生解决临床与科研问题的有力工具。这里罗列医学转录组学相关的几个概念&#xff0c;从使用者&#xff08;医生&#xff09;的角度看待理解相关技术&#xff0c;为后续使用该技术说明临床和科研问题奠定基础。…

    量子机器学习中的GPU加速实践:基于CUDA Quantum的混合编程模型探索

    引言&#xff1a;量子机器学习的新范式 在量子计算与经典机器学习交叉融合的前沿领域&#xff0c;量子机器学习&#xff08;Quantum Machine Learning, QML&#xff09;正经历着革命性突破。然而&#xff0c;随着量子比特规模的增长和算法复杂度的提升&#xff0c;传统计算架构…

    Matplotlib核心课程-2

    4.1 数据加载、储存 4.1.1 从数据文件读取数据 导入支持库&#xff1a; import numpy as np from pandas import Series,DataFrame import pandas as pd 从csv文件读取数据&#xff0c;一般方法&#xff1a; pd.read_csv(../data/ex1.csv,encodinggbk) 从csv文件读取数据&#…

    new和malloc的区别

    1 语义层级不同&#xff1a;语言机制 vs. 库函数 new / new[] (C 关键字)malloc / calloc / realloc (C 运行时函数)本质语言级运算符&#xff1b;可被重载库函数&#xff1b;无法重载作用分配内存 并调用构造函数仅分配原始字节块&#xff0c;不做初始化&#xff0c;也不调用…

    C++11新特性_自动类型推导_auto

    在 C11 标准中&#xff0c;auto关键字被赋予了全新且强大的功能&#xff0c;它用于自动类型推导&#xff0c;即编译器能够根据变量的初始化表达式自动确定其类型。 基本语法 使用auto声明变量时&#xff0c;只需给出auto关键字&#xff0c;后面紧跟变量名&#xff0c;并对其进…

    [预备知识]6. 优化理论(二)

    优化理论 本章节介绍深度学习中的高级优化技术&#xff0c;包括学习率衰减、梯度裁剪和批量归一化。这些技术能够显著提升模型的训练效果和稳定性。 学习率衰减&#xff08;Learning Rate Decay&#xff09; 数学原理与可视化 学习率衰减策略的数学表达&#xff1a; 步进式…