MyBatis缓存详解:一级缓存、二级缓存与实战优化

引入:

在高并发系统中,缓存是提升查询性能的核心手段之一。MyBatis 内置了两级缓存机制,能有效减少数据库交互次数,降低系统开销。本文将从"缓存基础"到"MyBatis 一/二级缓存的实战配置、失效场景",全面解析 MyBatis 缓存的核心逻辑。

一、缓存基础:是什么?为什么用?

1.1 什么是缓存?

缓存是存储在内存中的数据,将用户高频访问的数据暂存于内存,避免每次查询都从磁盘(数据库)读取,从而提升查询效率。

1.2 为什么使用缓存?

  1. 减少与数据库的交互次数,降低数据库压力;
  2. 降低系统 IO 开销,提升接口响应速度;
  3. 解决高并发场景下的性能瓶颈。

1.3 什么样的数据适合缓存?

高频查询 + 低频修改的数据(如字典表、配置信息等),不适合缓存"实时性要求高、频繁修改"的数据(如订单状态)。

二、MyBatis 缓存体系:一级缓存 + 二级缓存

MyBatis 默认提供两级缓存:

  • 一级缓存:SqlSession 级别(默认开启);
  • 二级缓存:SqlSessionFactory 级别(需手动配置)。

2.1 一级缓存(本地缓存)

2.1.1 核心概念

一级缓存是 SqlSession 级别的缓存:同一次 SqlSession 会话中,查询到的数据会被存入本地缓存;后续相同查询会直接从缓存获取,无需访问数据库。

2.1.2 实战测试一级缓存

```java

java 复制代码
@Test

public void testFirstLevelCache() throws IOException {

// 1. 加载配置,创建SqlSessionFactory

InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);

// 2. 创建SqlSession(同一会话)

SqlSession session = factory.openSession();

UserDao mapper = session.getMapper(UserDao.class);

// 3. 第一次查询(访问数据库)

User user1 = mapper.findById(1);

System.out.println(user1);

// 4. 第二次查询(从一级缓存获取)

User user2 = mapper.findById(1);

System.out.println(user2);

// 5. 验证缓存(对象地址相同)

System.out.println("缓存命中:" + (user1 == user2)); // 输出true

// 6. 释放资源

session.close();

in.close();

}

```

2.1.3 一级缓存失效的4种场景

  1. SqlSession 不同:不同 SqlSession 属于不同缓存空间;
  2. 查询条件不同:缓存按"查询语句 + 参数"唯一标识,条件不同则缓存不命中;
  3. 两次查询间执行增删改操作:增删改会触发缓存清空(保证数据一致性);
  4. 手动清除缓存:调用 session.clearCache() 主动清空缓存。

2.2 二级缓存(全局缓存)

2.2.1 核心概念

二级缓存是 SqlSessionFactory 级别的缓存:同一 SqlSessionFactory 创建的 SqlSession,查询数据会存入二级缓存;跨 SqlSession 共享缓存数据。

2.2.2 开启二级缓存的4个条件

  1. 全局配置开启缓存:在 SqlMapConfig.xml 中开启全局缓存开关;
  2. Mapper 映射文件声明缓存:在对应 Mapper.xml 中配置 ;
  3. 实体类实现序列化:缓存数据需序列化存储(防止内存溢出);
  4. 二级缓存必须在SqlSession关闭(一级缓存)或提交(二级缓存)之后有效

2.2.3 实战配置二级缓存

步骤1:全局配置开启缓存(SqlMapConfig.xml)

```xml

XML 复制代码
<!‐‐ 开启二级缓存 ‐‐>
<settings>
    <!--开启二级缓存-->
    <setting name="cacheEnabled" value="true"/>
</settings>

```

步骤2:Mapper 映射文件声明缓存(UserDao.xml)

```xml

XML 复制代码
<!--使用二级缓存-->
<cache/>

```

步骤3:实体类实现序列化

```java

java 复制代码
public class User implements Serializable {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
    // get set方法 .....
 }

```

步骤4:测试二级缓存

```java

java 复制代码
@Test

public void testSecondLevelCache() throws IOException {

InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);

// 第一个SqlSession(查询后关闭,将数据写入二级缓存)

SqlSession session1 = factory.openSession();

UserDao mapper1 = session1.getMapper(UserDao.class);

User user1 = mapper1.findById(1);

session1.close(); // 关闭后写入二级缓存

// 第二个SqlSession(从二级缓存获取)

SqlSession session2 = factory.openSession();

UserDao mapper2 = session2.getMapper(UserDao.class);

User user2 = mapper2.findById(1);

session2.close();

// 验证缓存(数据相同,但对象地址不同)

System.out.println("数据一致:" + user1.equals(user2)); // true

System.out.println("对象地址不同:" + (user1 == user2)); // false

}

```

2.2.4 二级缓存的重要结论

二级缓存存储的是**数据的序列化拷贝**,而非对象本身,因此两次查询的对象地址不同,但数据内容一致。

2.2.5 二级缓存的参数配置

`` 标签支持自定义缓存策略,常用参数:

```xml

XML 复制代码
<cache eviction="FIFO"

flushInterval="60000"

size="512"

readOnly="true" />

```

三、MyBatis 缓存查询顺序

MyBatis 会按以下顺序查询缓存:

  1. 先查二级缓存(全局共享,优先命中);
  2. 二级缓存未命中 → 查一级缓存(当前 SqlSession 缓存);
  3. 一级缓存未命中 → 访问数据库;
  4. 数据库查询结果 → 存入一级缓存;
  5. SqlSession 关闭/提交 → 一级缓存数据写入二级缓存。

四、总结:MyBatis 缓存的最佳实践

  1. 一级缓存:默认开启,无需额外配置,适用于"短会话、高频重复查询"场景;
  2. 二级缓存:适用于"跨会话共享数据"场景,需注意"序列化、缓存失效规则";
  3. 缓存失效:增删改操作会清空对应缓存,保证数据一致性;
  4. 生产建议:若系统已使用 Redis 等分布式缓存,可关闭 MyBatis 二级缓存,避免缓存层级混乱。

MyBatis 缓存是提升查询性能的轻量方案,掌握其核心逻辑与失效场景,能帮助你在实际项目中合理利用缓存,避免"缓存穿透、缓存脏数据"等问题。

相关推荐
PacosonSWJTU2 小时前
Guava缓存使用入门
java·缓存·guava
胡闹543 小时前
MyBatis-Plus 更新字段为 null 为何失效?
java·数据库·mybatis
侠客行03173 小时前
Mybatis入门到精通 二
java·mybatis·源码阅读
侠客行031714 小时前
Mybatis入门到精通 一
java·mybatis·源码阅读
java1234_小锋17 小时前
Redis的热Key问题如何解决?
数据库·redis·缓存
鸽鸽程序猿17 小时前
【Redis】事务
数据库·redis·缓存
赵得C18 小时前
完整 Oracle 12c 分页 Demo(Spring Boot+MyBatis+PageHelper)
spring boot·oracle·mybatis
今晚务必早点睡19 小时前
Redis——快速入门第七课:Redis 为什么这么快?
数据库·redis·缓存
任子菲阳1 天前
学Javaweb第四天——springboot入门
java·spring·mybatis