MyBatis--缓存详解

一、 一级缓存(Local Cache)

1.1 概念

  • 作用域SqlSession 级别(默认开启,不能关闭)。

  • 原理 :一次查询结果会放到当前 SqlSession 的 HashMap 中,后续相同 SQL(相同参数、相同 StatementId)会直接从缓存取,而不会再查数据库。

  • 失效场景

    1. SqlSession 关闭。

    2. SqlSession 执行了 INSERTUPDATEDELETE 操作(缓存会清空)。

    3. 查询参数不同或 SQL 不同。

    4. 手动调用 clearCache()

👉 一级缓存属于 会话缓存,在 Spring Boot 中,由于每次调用 Mapper 方法默认都会使用新的 SqlSession,所以在事务范围内能体现。


二、二级缓存(Global Cache)

2.1 概念

  • 作用域SqlSessionFactory 级别(即 Mapper namespace 级别),可被多个 SqlSession 共享。

  • 默认情况:全局关闭,需要手动配置。

  • 原理:查询结果会被序列化后存储在二级缓存区域(内存或 Redis 等),不同 SqlSession 之间共享缓存。

  • 失效场景

    1. 执行增删改操作时,清空当前 namespace 下的缓存。

    2. 缓存达到最大 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

java 复制代码
mybatis:
  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

java 复制代码
spring:
  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 达上限
常见用途 单次事务内减少重复查询 跨会话共享查询结果,提高性能
相关推荐
MZ_ZXD0012 小时前
springboot汽车租赁服务管理系统-计算机毕业设计源码58196
java·c++·spring boot·python·django·flask·php
天涯海风4 小时前
检索增强生成(RAG) 缓存增强生成(CAG) 生成中检索(RICHES) 知识库增强语言模型(KBLAM)
人工智能·缓存·语言模型
不羁。。5 小时前
【撸靶笔记】第八关:GET - Blind - Boolian Based - Single Quotes
数据库·sql·mybatis
m0_595199856 小时前
Redis(以Django为例,含具体操作步骤)
数据库·redis·缓存
Monly219 小时前
RabbitMQ:生产者可靠性(生产者重连、生产者确认)
spring boot·rabbitmq·java-rabbitmq
ankleless10 小时前
Spring Boot 实战:从项目搭建到部署优化
java·spring boot·后端
西红柿维生素12 小时前
MyBatis SqlCommand+MethodSignature源码探究
mybatis
白露与泡影13 小时前
SpringBoot前后端token自动续期方案
spring boot·后端·状态模式
还听珊瑚海吗15 小时前
基于WebSocket和SpringBoot聊天项目ChatterBox测试报告
spring boot·websocket·网络协议