专业编程教程与实战项目分享平台

网站首页 > 技术文章 正文

深入解析 MyBatis 中的缓存机制_mybatis有几层缓存

ins518 2025-10-19 05:51:31 技术文章 3 ℃ 0 评论

在当今互联网软件开发领域,性能优化始终是开发者们关注的重点。MyBatis 作为一款广泛使用的 Java 持久层框架,其缓存机制为提升应用性能提供了有力支持。通过缓存,我们能够减少对数据库的频繁访问,从而显著提高系统的响应速度和整体性能。接下来,让我们一同深入探究 MyBatis 中的缓存机制。

MyBatis 缓存初相识

想象一下,你每天上班都要去公司附近的咖啡店买咖啡。第一天,你点了一杯拿铁,咖啡店需要现做,花费了一些时间。但第二天,当你再次点同样的拿铁时,店员直接从准备好的 “缓存” 中拿给你,瞬间就完成了交易。MyBatis 的缓存机制就如同这家咖啡店的 “缓存策略”。当应用程序通过 MyBatis 发起查询时,MyBatis 会先去缓存中查找是否有符合条件的结果。如果有,就直接从缓存中获取数据返回给应用程序,无需再次查询数据库;若没有,则查询数据库,然后将结果存入缓存,以便下次使用。

一级缓存:SqlSession 的贴心助手

(一)一级缓存的作用范围与特点

一级缓存也被称为本地缓存,它是 MyBatis 中默认开启的缓存,作用域为 SqlSession 级别。这意味着,在同一个 SqlSession 执行相同的 SQL 查询时,MyBatis 会从一级缓存中获取结果,而不是再次访问数据库。其生命周期与 SqlSession 相同,当 SqlSession 关闭时,对应的一级缓存也会被清空。

从存储方式来看,一级缓存是基于 PerpetualCache 和 HashMap 实现的,它将查询结果以键值对的形式存储,其中键为 SQL 语句的字符串及其参数,值为查询结果。

(二)一级缓存的失效条件

  1. 数据修改操作:在 SqlSession 中,一旦执行了 INSERT、UPDATE、DELETE 操作,为了确保数据的一致性,一级缓存会被清空。因为这些操作可能会使原本缓存的数据变得不再准确。
  2. 手动清空缓存:开发者可以调用 SqlSession.clearCache () 方法手动清空一级缓存。
  3. SqlSession 关闭:当 SqlSession 关闭后,一级缓存也随之失效。

(三)一级缓存的示例代码

SqlSession session = sqlSessionFactory.openSession();
User user1 = session.selectOne("selectUserById", 1); // 从数据库查询
User user2 = session.selectOne("selectUserById", 1); // 从一级缓存中获取结果

在上述代码中,第一次查询时,由于缓存中没有数据,MyBatis 会查询数据库,并将结果存入一级缓存。第二次执行相同查询时,MyBatis 发现一级缓存中有符合条件的数据,便直接从缓存中获取,大大提高了查询效率。

(四)一级缓存的适用场景

用户会话场景:在 Web 应用中,每个用户的会话都是独立的。如果用户在会话期间多次请求相同的数据,例如用户在浏览商品详情页时,可能多次请求商品信息,一级缓存可以确保这些请求不需要每次都查询数据库,从而提高性能。

批量操作场景:在进行批量插入或更新操作时,一级缓存可以帮助我们减少数据库的访问次数。例如,在一个订单系统中,当用户下单后,系统需要批量插入订单详情,一级缓存可以缓存这些插入操作的结果,减少数据库的压力。

二级缓存:Mapper 级别的性能利器

(一)二级缓存的作用范围与特点

二级缓存是基于 Mapper 级别的缓存,它可以在多个 SqlSession 之间共享缓存数据。即只要是同一个 Mapper 的查询,都可以使用二级缓存中的数据。与一级缓存不同,二级缓存默认是关闭的,需要在配置文件或 Mapper 文件中显式开启。

由于二级缓存是跨 SqlSession 的,所以缓存对象必须实现 Serializable 接口,以便进行序列化存储。而且,只有在事务提交后,查询结果才会写入二级缓存。

(二)二级缓存的失效条件

与一级缓存类似,当执行 INSERT、UPDATE、DELETE 等修改操作时,二级缓存中对应 namespace 下的缓存会被清空。因为这些操作可能导致相关数据发生变化,缓存中的数据不再准确。

(三)启用二级缓存的步骤

在 MyBatis 全局配置文件中启用二级缓存

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

在具体的 Mapper 文件中配置二级缓存

<mapper namespace="com.example.mapper.UserMapper">
    <!-- 启用二级缓存 -->
    <cache />
    <select id="selectUserById" resultType="User">
        SELECT * FROM user WHERE id = #{id}
    </select>
</mapper>

确保实体类实现 Serializable 接口:因为缓存数据需要进行序列化存储,所以实体类必须实现 Serializable 接口。例如:

public class User implements Serializable {
    private int id;
    private String name;
    // Getters and Setters
}

(四)二级缓存的示例代码

// 第一次查询,结果存入二级缓存
try (SqlSession session1 = sqlSessionFactory.openSession()) {
    UserMapper mapper1 = session1.getMapper(UserMapper.class);
    User user1 = mapper1.selectUserById(1);
}

// 第二次查询,不同的SqlSession实例,结果从二级缓存中获取
try (SqlSession session2 = sqlSessionFactory.openSession()) {
    UserMapper mapper2 = session2.getMapper(UserMapper.class);
    User user2 = mapper2.selectUserById(1);
}

在上述代码中,第一次查询时,由于二级缓存中没有数据,MyBatis 会查询数据库,并将结果存入二级缓存。第二次使用不同的 SqlSession 进行相同查询时,MyBatis 会直接从二级缓存中获取数据,减少了数据库的访问次数。

(五)二级缓存的适用场景

读多写少的业务场景:对于那些读操作远多于写操作的业务,二级缓存可以显著提高性能。例如,一个新闻网站,新闻内容一旦发布,就会被频繁地读取,但更新频率较低。通过二级缓存,可以在多个用户请求时提供快速的数据访问。

分布式系统场景:在分布式系统中,不同服务可能会访问相同的数据。二级缓存可以跨服务共享,减少对数据库的访问压力。例如,在微服务架构中,用户服务和订单服务可能都需要访问商品信息,通过二级缓存,可以确保这些服务访问的是相同的数据,同时减少数据库的负载。

数据一致性要求不高的业务场景:对于那些对数据一致性要求不高的业务,二级缓存可以提供更快的读取速度。例如,一些统计数据或报表数据,它们不需要实时更新,但需要快速读取,二级缓存可以满足这种需求。

一级缓存与二级缓存的比较

特性

一级缓存

二级缓存

作用范围

SqlSession 级别

Mapper 映射器级别,跨 SqlSession

默认状态

默认开启

默认关闭

缓存生命周期

与 SqlSession 相同

与 Mapper 相同

缓存失效

SqlSession 执行写操作时清空

Mapper 执行写操作时清空

缓存共享

不同的 SqlSession 之间不共享

不同的 SqlSession 之间共享

存储要求

无特殊要求

缓存对象需实现 Serializable 接口

配置和管理二级缓存

MyBatis 为我们提供了多种方式来配置和管理二级缓存,以满足不同的业务需求。

(一)缓存策略

可以通过<cache>标签的eviction属性配置缓存回收策略。默认使用 LRU(最近最少使用)策略,即移除最长时间不被使用的对象。此外,还有其他选项:

  1. FIFO(先进先出):按对象进入缓存的顺序移除,先进入缓存的对象先被移除。
  2. SOFT(软引用):基于垃圾收集器状态和软引用规则移除对象,在内存不足时,软引用关联的对象会被回收。
  3. WEAK(弱引用):更积极地移除对象,同样基于垃圾收集器状态和弱引用规则,只要垃圾收集器扫描到弱引用关联的对象,就会回收它。

(二)刷新间隔

通过flushInterval属性配置缓存的刷新间隔,单位为毫秒。默认情况下不会自动刷新,我们可以根据业务需求设置合适的刷新间隔,例如:

<cache eviction="LRU" flushInterval="60000" />

上述配置表示每 60000 毫秒(即 1 分钟)自动刷新一次缓存。

(三)缓存大小

通过size属性设置缓存的最大对象数量。例如:

<cache eviction="LRU" flushInterval="60000" size="1024" />

这表示该缓存最多可以存储 1024 个对象,当缓存达到这个数量后,再添加新对象时,会根据缓存回收策略移除一些旧对象。

(四)只读模式

通过readOnly属性设置缓存是否为只读模式。当设置为true时,缓存中的对象在缓存期间不可修改,这样可以提高并发性能,因为多个线程可以共享同一个缓存对象,而无需担心数据冲突。例如:

<cache eviction="LRU" flushInterval="60000" size="1024" readOnly="true" />

总结

MyBatis 的缓存机制为我们提供了强大的性能优化手段。一级缓存基于 SqlSession,适用于在同一个会话中的多次查询,能有效减少数据库访问次数;二级缓存基于 Mapper,可在多个会话间共享缓存数据,尤其适用于读多写少的业务场景以及分布式系统。通过合理配置和管理缓存,我们能够显著提升应用程序的性能,降低数据库负载,为用户提供更流畅、高效的服务体验。在实际开发中,我们应根据业务需求和数据特点,灵活运用 MyBatis 的缓存机制,让我们的应用程序在性能方面更上一层楼。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表