Mysql--实战篇--mybatis cache(一级缓存,二级缓存,子查询主键主查询全部,查询条件加索引,覆盖索引等)

news/2025/2/19 3:27:38/

MyBatis是一个流行的Java持久层框架,它简化了JDBC的使用,允许开发者通过XML或注解的方式定义SQL语句,并将结果映射到Java对象。为了提高查询性能,MyBatis提供了缓存机制,可以在一定程度上减少数据库的访问次数,从而提升应用的响应速度。

MyBatis缓存分为两种类型:

1、一级缓存(SqlSession级别缓存

一级缓存是默认开启的,它是基于SqlSession的缓存。也就是说,只要在一个SqlSession的生命周期内执行相同的查询语句,并且参数和SQL语句完全相同,MyBatis会从缓存中返回之前查询的结果,而不会再次访问数据库。

(1)、SqlSession

SqlSession是MyBatis框架中的核心接口之一,它代表了Mybatis与数据库的一次会话(Session)。通过SqlSession,你可以执行SQL语句、管理事务、获取映射器(Mapper)等。它是MyBatis与数据库交互的主要入口,类似于JDBC中的Connection对象,但提供了更高层次的抽象和更简洁的API。

SqlSession的作用:

  • 执行SQL语句:通过SqlSession,你可以执行查询、插入、更新和删除操作。
  • 管理事务:SqlSession提供了对事务的控制,允许你开始、提交或回滚事务。
  • 获取Mapper接口:SqlSession可以根据Mapper接口生成对应的代理对象,方便你通过接口调用SQL语句。
  • 缓存管理:SqlSession内部维护了一级缓存,可以在同一个会话中重复使用查询结果,减少对数据库的访问。
  • 关闭资源:SqlSession是一个需要手动关闭的资源,确保在使用完毕后调用close()方法释放数据库连接和其他资源。

SqlSession的生命周期:
SqlSession 的生命周期通常与一次数据库操作相关联。具体如下:
1、创建SqlSession:通过SqlSessionFactory创建一个新的SqlSession实例。
2、执行操作:使用SqlSession执行SQL语句、管理事务或获取Mapper接口。
3、提交或回滚事务:根据业务逻辑决定是否提交或回滚事务。
4、关闭SqlSession:操作完成后,必须调用close()方法关闭SqlSession,以释放数据库连接和其他资源。

简单理解:
当服务端通过Mybatis操作数据库时,会先创建一个SqlSession对象,记录本次和数据库的会话信息。本次会话过程中,会将所有第一次查询sql的结果记录到SqlSession的一级缓存中。之后如果有一模一样的查询sql,则直接取一级缓存的结果而不会再次请求数据库。当本次事务提交或者回滚后,SqlSession就会被清理掉。即便下次在有和之前请求一模一样的查询sql,也会创建新的SqlSession在继续工作。
注意:
多线程的请求,每个线程也也都是创建自己独立的SqlSession,不会彼此共享。

(2)、一级缓存的工作原理

  • 缓存范围:一级缓存的作用范围是单个SqlSession。每个SqlSession都有自己的缓存,不同的SqlSession之间不会共享缓存
  • 缓存失效:当SqlSession执行了任何增删改操作(如insert、update、delete)时,一级缓存会自动清空,以确保后续查询能够获取最新的数据。
  • 事务影响:在同一个SqlSession中,如果开启了事务,那么在事务提交或回滚之前,一级缓存中的数据不会被清除。只有当事务结束时,缓存才会被清空。

(3)、示例说明

假设我们有一个UserMapper接口,用于查询用户信息:

public interface UserMapper {@Select("SELECT * FROM users WHERE id = {id}")User getUserById(Long id);
}

解释:
在同一个SqlSession中,连续两次调用getUserById(1),MyBatis会在第一次查询时将结果存储到一级缓存中,第二次查询时直接从缓存中读取,而不会再次访问数据库。(如:通过@Transactional开启了事务的代码块中,调用两次getUserById(1)方法)

(4)、注意事项

  • 一级缓存的作用范围有限,仅限于单个SqlSession。如果你使用的是Spring的事务管理器(如@Transactional注解),那么SqlSession的生命周期通常与事务的生命周期一致。
  • 如果你手动关闭或提交SqlSession,一级缓存也会被清空。

2、二级缓存(Mapper级别缓存

二级缓存是跨SqlSession的缓存,它可以作用于多个SqlSession之间的查询结果。
二级缓存的作用范围是整个应用程序中使用相同Mapper的所有SqlSession。通过启用二级缓存,可以减少对数据库的频繁访问,提升查询性能。

简单理解:
对于同一个Mapper接口,所有的查询请求不论是哪一个Sqlsession执行的查询操作,都会在第一次查询后将查询结果保存到二级缓存中。之后只要是相同的查询sql就会直接从二级缓存中取数据而不会在请求数据库了。
对于不同的Mapper接口,彼此的缓存是彼此隔离的。

(1)、工作原理

  • 缓存范围:二级缓存的作用范围是整个应用程序中使用相同Mapper的所有SqlSession。不同Mapper之间的缓存是隔离的,不会相互影响。
  • 缓存失效:当某个SqlSession执行了与该Mapper相关的增删改操作时,二级缓存会自动清空,以确保后续查询能够获取最新的数据。
  • 缓存配置:二级缓存需要显式开启,并且可以通过XML或注解进行配置。也可以为每个Mapper单独配置缓存策略,也可以全局配置缓存

(2)、配置方式

1、XML 配置

在XML文件中,使用标签来启用二级缓存。你可以指定缓存的类型、大小、清除策略等。

<mapper namespace="com.example.mapper.UserMapper"><!-- 启用二级缓存 --><cache eviction="LRU"  <!-- 清除策略:FIFOLRUSOFTWEAK -->flushInterval="60000"  <!-- 自动刷新间隔(毫秒),默认不自动刷新 -->size="512"  <!-- 缓存的最大条目数 -->readOnly="false"  <!-- 是否只读,默认 false -->/><select id="getUserById" resultType="User">SELECT * FROM users WHERE id = {id}</select>
</mapper>

参数说明:

  • eviction:清除策略,决定了当缓存达到最大容量时如何移除旧的数据。常见的清除策略有:
    • FIFO:先进先出,最早进入缓存的数据将被最先移除。
    • LRU:最近最少使用,最长时间未被使用的数据将被移除。
    • SOFT:软引用,依赖于JVM的垃圾回收机制,只有在内存不足时才会被回收。
    • WEAK:弱引用,只要GC发生,就会被回收。
  • flushInterval:自动刷新间隔(以毫秒为单位)。设置后,MyBatis会在指定的时间间隔后自动清空二级缓存。如果你希望二级缓存一直有效,可以不设置此参数(即默认不自动刷新)。
  • size:缓存的最大条目数。超过这个数量时,缓存会根据eviction策略移除旧的数据。
  • readOnly:是否只读。如果设置为true,则缓存中的数据被认为是只读的,不会因为数据的变化而自动失效。这可以提高性能,但需要注意的是,如果数据在数据库中发生了变化,缓存中的数据可能会变得不一致。因此,readOnly=true通常适用于那些不经常变化的只读数据。

上述示例仅包含一个查询语句(select),如果该xml中包含多个select,也都是一样生效的。

2、注解配置

你也可以使用 @CacheNamespace注解来启用二级缓存。这个注解可以放在Mapper接口上。
示例:

import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.annotations.CacheNamespace;@CacheNamespace(implementation = org.apache.ibatis.cache.decorators.FifoCache.class,  // 缓存实现类eviction = "FIFO",  // 清除策略flushInterval = 60000,  // 自动刷新间隔(毫秒)size = 512,  // 缓存的最大条目数readOnly = false // 是否只读
)
public interface UserMapper {@Select("SELECT  FROM users WHERE id = {id}")User getUserById(Long id);
}
3、全局配置

在application.properties文件中添加配置。
配置示例:

# 启用二级缓存
mybatis.configuration.cache-enabled=true
# 启用懒加载
mybatis.configuration.lazy-loading-enabled=true
# 禁用激进的懒加载
mybatis.configuration.aggressive-lazy-loading=false
# 指定Mapper XML文件的位置
mybatis.mapper-locations=classpath:mapper/.xml

说明:

  • cache-enabled:用于启用或禁用二级缓存。默认值为true,表示启用二级缓存
  • lazy-loading-enabled和aggressive-lazy-loading:这两个配置项与懒加载有关,虽然它们不是直接控制二级缓存的,但会影响缓存的行为。如果你启用了懒加载,可能会导致二级缓存中的数据延迟加载,影响性能。
4、引入第三方系统

可以使用第三方缓存系统(如Redis或Ehcache)作为MyBatis的二级缓存
这里导入Redis的依赖和配置Redis的连接就不说了。

使用示例:

<mapper namespace="com.example.mapper.UserMapper"><!-- 使用 Redis 作为二级缓存 --><cache type="org.mybatis.caches.redis.RedisCache"/><select id="getUserById" resultType="User">SELECT  FROM users WHERE id = {id}</select>
</mapper>

或者也可以使用注解:

import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.annotations.CacheNamespace;@CacheNamespace(implementation = RedisCache.class, eviction = RedisCache.class)
public interface UserMapper {@Select("SELECT  FROM users WHERE id = {id}")User getUserById(Long id);
}

(3)、缓存清除二级缓存

  • 手动清除缓存:你可以通过clearCache()方法手动清除当前SqlSession的一级缓存和二级缓存
    示例:
try (SqlSession session = sqlSession.openSession()) {UserMapper mapper = session.getMapper(UserMapper.class);// 查询用户User user = mapper.getUserById(1);System.out.println(user);// 手动清除缓存mapper.clearCache();// 再次查询用户,此时会重新访问数据库User user2 = mapper.getUserById(1);System.out.println(user2);
}
  • 自动清除缓存:当执行与该Mapper相关的增删改操作时,MyBatis会自动清除二级缓存,以确保后续查询能够获取最新的数据。

(4)、二级缓存注意事项

  • 缓存一致性问题:二级缓存的作用范围是跨SqlSession的,因此可能会导致缓存中的数据与数据库中的数据不一致。为了避免这种情况,建议在多写少读的场景下谨慎使用二级缓存,或者设置合理的缓存清除策略。
  • 只读缓存:如果你将readOnly设置为true,则缓存中的数据被认为是只读的,MyBatis不会尝试更新缓存中的数据。这可以提高性能,但需要注意数据的一致性问题。
  • 缓存粒度:二级缓存的作用范围是整个Mapper,因此所有使用该Mapper的查询都会共享同一个缓存。如果你希望更细粒度地控制缓存,可以考虑使用自定义缓存实现。

(5)、二级缓存总结

  • 二级缓存是跨多个SqlSession的缓存机制,适用于多个线程或请求之间的数据共享。
  • 每个线程请求相同ID时,如果二级缓存中已经存在相应的数据,MyBatis会优先从二级缓存中获取数据,而不是再次查询数据库。
  • 多个select语句会共享同一个二级缓存,但MyBatis会根据查询的参数和SQL语句的不同,生成不同的CacheKey,以确保不同的查询不会互相干扰。
  • 二级缓存适合用于只读数据,对于频繁更新的数据,使用二级缓存可能会导致数据不一致的问题。

3、Mybatis一级缓存和二级缓存总结

  • 一级缓存优先于二级缓存:MyBatis会首先检查一级缓存,如果一级缓存中存在数据,则直接返回缓存中的结果,而不会去检查二级缓存
  • 一级缓存的作用范围:仅限于当前SqlSession,不同的SqlSession之间不会共享一级缓存
  • 二级缓存的作用范围:跨越多个SqlSession,适用于多个线程或请求之间的数据共享。
  • 缓存的触发顺序:MyBatis会先检查一级缓存,再检查二级缓存,最后才执行SQL查询。

对比如下:
在这里插入图片描述

乘风破浪会有时,直挂云帆济沧海!!!


http://www.ppmy.cn/news/1564923.html

相关文章

Spring Boot中的配置文件有哪些类型

在 Spring Boot 中&#xff0c;配置文件用于管理应用程序的设置和参数&#xff0c;通常存放在项目的 src/main/resources 目录下。Spring Boot 支持多种类型的配置文件&#xff0c;并通过这些文件来控制应用的行为和环境配置。 1. application.properties application.proper…

数学基础 --线性代数之理解矩阵乘法

理解矩阵乘法的解析 矩阵乘法&#xff08;Matrix Multiplication&#xff09;是线性代数中的核心操作之一。在数学、几何和工程实际中&#xff0c;它不仅是一种代数运算规则&#xff0c;还承载着丰富的几何和映射意义。本文将从多个角度深入解析矩阵乘法&#xff0c;帮助读者理…

ros2-7.5 做一个自动巡检机器人

7.5.1 需求及设计 又到了小鱼老师带着做最佳实践项目了。需求&#xff1a;做一个在各个房间不断巡逻并记录图像的机器人。 到达目标点后首先通过语音播放到达目标点信息&#xff0c; 再通过摄像头拍摄一张图片保存到本地。 7.5.2 编写巡检控制节点 在chapt7_ws/src下新建功…

【解锁新技能!Flux.1如何实现远程AI生成图像随时随地创作】

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

20250120面试鸭特训营第28天

更多特训营笔记详见个人主页【面试鸭特训营】专栏 250120 1. 说说 Java 中 HashMap 的原理&#xff1f; HashMap 的底层结构 HashMap 底层由 node 数组、单链表、红黑树构成。根据哈希函数计算得到哈希值&#xff0c;哈希值确定了元素保存在 node 数组中的具体下标。HashMap…

Ghost硬盘对拷教程分享

Ghost32是一款老古董级别的备份和恢复软件&#xff0c;通常用于创建系统镜像以及恢复系统。它最初由Symantec公司开发&#xff0c;用于在计算机系统上进行备份、克隆和恢复操作。 由于这个软件早已停止更新很多年了&#xff0c;并且也是全英文的用户界面&#xff0c;对国内用户…

山西省乡镇界面图层shp格式arcgis数据乡镇名称和编码2020年wgs84坐标无偏移测评

这篇文档将深入解析标题和描述中提及的IT知识点&#xff0c;主要关注地理信息系统&#xff08;GIS&#xff09;和ArcGIS软件的应用&#xff0c;以及shp文件格式的相关知识。 我们要理解"山西省乡镇界面图层shp格式arcgis数据乡镇名称和编码2020年wgs84坐标无偏移.zip&quo…

[HDCTF2019]Maze

[HDCTF2019]Maze 一、查壳 有壳upx&#xff0c;32位&#xff0c;解壳 二、IDA分析 刚一点进去就发现花指令&#xff0c;不出意外无法F5. 花指令&#xff0c;其特征就是在jnz后面call里面出现非法指令。&#xff08;会爆红&#xff09; 所以我们现在要解决花指令 三、解决…