一、 一级缓存(Local Cache)
1.1 概念
作用域 :
SqlSession
级别(默认开启,不能关闭)。原理 :一次查询结果会放到当前 SqlSession 的
HashMap
中,后续相同 SQL(相同参数、相同 StatementId)会直接从缓存取,而不会再查数据库。失效场景:
SqlSession 关闭。
SqlSession 执行了
INSERT
、UPDATE
、DELETE
操作(缓存会清空)。查询参数不同或 SQL 不同。
手动调用
clearCache()
。👉 一级缓存属于 会话缓存,在 Spring Boot 中,由于每次调用 Mapper 方法默认都会使用新的 SqlSession,所以在事务范围内能体现。
二、二级缓存(Global Cache)
2.1 概念
作用域 :
SqlSessionFactory
级别(即 Mapper namespace 级别),可被多个 SqlSession 共享。默认情况:全局关闭,需要手动配置。
原理:查询结果会被序列化后存储在二级缓存区域(内存或 Redis 等),不同 SqlSession 之间共享缓存。
失效场景:
执行增删改操作时,清空当前 namespace 下的缓存。
缓存达到最大 size 或超时。
👉 二级缓存适合读多写少的场景。分布式系统中常用 Redis 代替内存缓存。
三、 一级缓存示例(Spring Boot 默认行为)
UserMapper.java
java@Mapper public interface UserMapper { User findById(Integer id); }
UserMapper.xml
XML<mapper namespace="com.example.mapper.UserMapper"> <select id="findById" parameterType="int" resultType="com.example.entity.User"> SELECT * FROM user WHERE id = #{id} </select> </mapper>
User 实体类
java@Data public class User implements Serializable { private Integer id; private String username; private Integer age; }
测试(验证一级缓存)
java@SpringBootTest class MybatisCacheTest { @Autowired private UserMapper userMapper; @Autowired private SqlSessionFactory sqlSessionFactory; @Test void testFirstLevelCache() { // 获取 SqlSession,手动控制事务,保证在同一个 SqlSession 中 try (SqlSession session = sqlSessionFactory.openSession()) { UserMapper mapper = session.getMapper(UserMapper.class); // 第一次查询 -> 查数据库 User u1 = mapper.findById(1); System.out.println(u1); // 第二次查询 -> 命中一级缓存,不走数据库 User u2 = mapper.findById(1); System.out.println(u2); // 执行插入 -> 会清空缓存 mapper.insert(new User(null, "Tom", 20)); // 第三次查询 -> 重新查数据库 User u3 = mapper.findById(1); System.out.println(u3); } } }
👉 在 同一个 SqlSession 内,多次执行相同查询会命中 一级缓存。
四、 二级缓存示例(Spring Boot 手动开启)
4.1 (本地 JVM 缓存):
application.yml
javamybatis: configuration: cache-enabled: true # 全局开启二级缓存 mapper-locations: classpath:mapper/*.xml
UserMapper.xml(启用二级缓存JVM)
XML<mapper namespace="com.example.mapper.UserMapper"> <!-- 开启二级缓存 --> <cache eviction="LRU" flushInterval="60000" size="512" readOnly="false"/> <select id="findById" parameterType="int" resultType="com.example.entity.User"> SELECT * FROM user WHERE id = #{id} </select> </mapper>
测试(验证二级缓存)
java@SpringBootTest class MybatisCacheTest { @Autowired private SqlSessionFactory sqlSessionFactory; @Test void testSecondLevelCache() { // 第一次查询:走数据库,结果写入二级缓存 try (SqlSession session1 = sqlSessionFactory.openSession()) { UserMapper mapper1 = session1.getMapper(UserMapper.class); User user1 = mapper1.findById(9); System.out.println("第一次查询:" + user1); session1.close(); // 一级缓存失效,但二级缓存仍保存 } // 第二次查询:命中二级缓存(不查数据库) try (SqlSession session2 = sqlSessionFactory.openSession()) { UserMapper mapper2 = session2.getMapper(UserMapper.class); User user2 = mapper2.findById(9); System.out.println("第二次查询:" + user2); session2.close(); } } }
👉 在不同的 SqlSession 中也能复用缓存,因为二级缓存存在于 SqlSessionFactory 范围。
4.2 (不使用本地 JVM 缓存,而是使用 Redis**)**:
引入依赖
XML<dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-redis</artifactId> <version>1.0.0</version> </dependency>
application.yml
javaspring: datasource: url: jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver redis: host: 127.0.0.1 port: 6379
UserMapper.xml(启用二级缓存redis)
XML<mapper namespace="com.example.mapper.UserMapper"> <cache type="org.mybatis.caches.redis.RedisCache"/> <select id="findById" parameterType="int" resultType="com.example.entity.User"> SELECT * FROM user WHERE id = #{id} </select> </mapper>
测试(验证二级缓存)
java@SpringBootTest public class MybatisRedisCacheTest { @Autowired private SqlSessionFactory sqlSessionFactory; @Test public void testRedisCache() { try (SqlSession session1 = sqlSessionFactory.openSession(); SqlSession session2 = sqlSessionFactory.openSession()) { UserMapper mapper1 = session1.getMapper(UserMapper.class); System.out.println(mapper1.findById(1)); session1.close(); // 数据进入 Redis UserMapper mapper2 = session2.getMapper(UserMapper.class); System.out.println(mapper2.findById(1)); // 直接从 Redis 命中 } } }
4.3使用本地 JVM 缓存和Redis的对比
4.3.1 原生的二级缓存(没有 Redis 的时候)
存储位置 :二级缓存是基于 Mapper(namespace)级别 的,存储在 当前应用 JVM 的内存 里(默认实现是
PerpetualCache
+HashMap
)。作用范围:同一个 Mapper 下,多个 SqlSession 之间可以共享缓存。
生命周期:缓存随着应用 JVM 存在,当应用重启时缓存会消失。
特点:
只能在 单机应用 下使用;
不能跨服务共享数据;
容量受限于 JVM 内存;
适合一些 读多写少的小型项目。
💡 举例:
应用 A 部署在服务器 X,查询了
UserMapper.findById(1)
,MyBatis 会把数据放到本地 JVM 内存的二级缓存中。
下次再查
findById(1)
(即使是新的 SqlSession),也可以直接从本地缓存拿到结果;但是如果还有一个应用 B(另一个服务实例),它就完全拿不到这个缓存。
👉 所以原生二级缓存本质上是"单机的 Mapper 级缓存"。
4.3.2 结合 Redis 的二级缓存(分布式)
存储位置 :MyBatis 不再把缓存放在 JVM 内存,而是存放到 Redis 这样的分布式缓存 里。
作用范围:不同服务实例(多个 JVM)之间都能共享缓存。
生命周期:缓存由 Redis 管理,可以持久化或设置 TTL(过期时间)。
特点:
支持 多机共享缓存,解决了单机缓存不一致的问题;
Redis 提供了更大的存储容量和高可用机制;
适合 分布式集群环境。
💡 举例:
应用 A 部署在服务器 X,应用 B 部署在服务器 Y。
A 查询
UserMapper.findById(1)
,结果会缓存到 Redis;B 查询
UserMapper.findById(1)
,即使它的 JVM 里没缓存,也能从 Redis 里拿到数据。👉 所以结合 Redis 后,二级缓存从"单机 JVM 缓存"变成了"分布式共享缓存"。
4.3.3 对比总结
特性 原生二级缓存(默认) 分布式二级缓存(结合 Redis) 存储位置 本地 JVM 内存 Redis(独立缓存服务器) 作用范围 单个应用实例 多个应用实例共享 生命周期 应用关闭即失效 Redis 可持久化 / TTL 控制 并发支持 单机缓存,受 JVM 限制 高并发支持,Redis 分布式存储 使用场景 小型单体应用 分布式 / 微服务架构
✅ 结论:
以前(默认二级缓存) → 只是单机 JVM 内存里的
namespace
级缓存;现在(结合 Redis) → 变成了真正的"分布式共享缓存",跨服务可见,适合 Spring Boot + 微服务架构。
五、 总结对比
特性 一级缓存(Local Cache) 二级缓存(Global Cache) 作用域 SqlSession SqlSessionFactory / namespace 默认开启 ✅ 是 ❌ 否,需要配置 生命周期 SqlSession 存活期间 SqlSessionFactory 存活期间 是否共享 仅限同一 SqlSession 内 跨 SqlSession 共享 失效场景 SqlSession 关闭、增删改、手动 clearCache 增删改、超时、size 达上限 常见用途 单次事务内减少重复查询 跨会话共享查询结果,提高性能
MyBatis--缓存详解
你我约定有三2025-08-22 3:09
相关推荐
MZ_ZXD0012 小时前
springboot汽车租赁服务管理系统-计算机毕业设计源码58196天涯海风4 小时前
检索增强生成(RAG) 缓存增强生成(CAG) 生成中检索(RICHES) 知识库增强语言模型(KBLAM)不羁。。5 小时前
【撸靶笔记】第八关:GET - Blind - Boolian Based - Single Quotesm0_595199856 小时前
Redis(以Django为例,含具体操作步骤)Monly219 小时前
RabbitMQ:生产者可靠性(生产者重连、生产者确认)ankleless10 小时前
Spring Boot 实战:从项目搭建到部署优化西红柿维生素12 小时前
MyBatis SqlCommand+MethodSignature源码探究白露与泡影13 小时前
SpringBoot前后端token自动续期方案还听珊瑚海吗15 小时前
基于WebSocket和SpringBoot聊天项目ChatterBox测试报告