3.1 MyBatis 缓存机制


文档摘要

3.1 MyBatis 缓存机制 MyBatis 缓存机制详解 3.1.1 MyBatis 缓存概述 MyBatis 的缓存机制主要分为两级: 一级缓存(本地缓存): SqlSession 级别的缓存,默认开启,生命周期与 SqlSession 一致。 二级缓存(全局缓存): Mapper 级别的缓存,需要手动开启和配置,多个 SqlSession 可以共享。 为什么需要缓存? 数据库访问通常是应用程序中最耗时的操作之一。 通过缓存,我们可以将经常访问的数据存储在内存中,下次需要相同数据时,直接从缓存中获取,避免了重复的数据库查询,从而显著提高应用程序的性能和响应速度。 缓存的优缺点: 优点: 提升查询性能,减少数据库压力。 降低网络延迟。 提高用户体验。

3.1 MyBatis 缓存机制

MyBatis 缓存机制详解

3.1.1 MyBatis 缓存概述

MyBatis 的缓存机制主要分为两级:

  • 一级缓存(本地缓存): SqlSession 级别的缓存,默认开启,生命周期与 SqlSession 一致。

  • 二级缓存(全局缓存): Mapper 级别的缓存,需要手动开启和配置,多个 SqlSession 可以共享。

为什么需要缓存?

数据库访问通常是应用程序中最耗时的操作之一。 通过缓存,我们可以将经常访问的数据存储在内存中,下次需要相同数据时,直接从缓存中获取,避免了重复的数据库查询,从而显著提高应用程序的性能和响应速度。

缓存的优缺点:

  • 优点:

    • 提升查询性能,减少数据库压力。

    • 降低网络延迟。

    • 提高用户体验。

  • 缺点:

    • 可能导致数据不一致,需要考虑缓存失效策略。

    • 增加了应用程序的复杂性。

    • 需要消耗额外的内存资源。

3.1.2 一级缓存 (Local Cache)

一级缓存是 MyBatis 内置的缓存,也称为本地缓存。它基于 SqlSession 存在,并且默认开启。

工作原理:

  1. 当 SqlSession 执行查询操作时,MyBatis 会先检查一级缓存中是否存在对应的缓存数据。

  2. 如果缓存命中,则直接从缓存中返回结果,避免数据库查询。

  3. 如果缓存未命中,则执行数据库查询,并将查询结果存入一级缓存。

  4. 当 SqlSession 关闭或执行更新、插入、删除操作时,一级缓存会被清空。

特点:

  • SqlSession 级别: 每个 SqlSession 都有自己独立的一级缓存。

  • 默认开启: 无需配置,自动生效。

  • 生命周期短: 与 SqlSession 的生命周期一致,SqlSession 关闭时缓存失效。

  • 不可控: 无法手动配置缓存失效策略。

代码实践:

假设我们有一个 UserMapper.xml 文件,包含以下查询语句:

<!-- UserMapper.xml --> <mapper namespace="com.example.mapper.UserMapper"> <select id="getUserById" resultType="com.example.entity.User"> SELECT * FROM user WHERE id = #{id} </select> </mapper>

Java 代码:

// 获取 SqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 获取 SqlSession try (SqlSession sqlSession = sqlSessionFactory.openSession()) { UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 第一次查询,会执行数据库查询 User user1 = userMapper.getUserById(1); System.out.println("First query: " + user1); // 第二次查询,直接从一级缓存获取 User user2 = userMapper.getUserById(1); System.out.println("Second query: " + user2); // 执行更新操作,清空一级缓存 user1.setName("New Name"); userMapper.updateUser(user1); sqlSession.commit(); // 第三次查询,会执行数据库查询 User user3 = userMapper.getUserById(1); System.out.println("Third query: " + user3); }

代码解释:

  1. 第一次查询 getUserById(1) 时,一级缓存为空,MyBatis 会执行数据库查询,并将结果放入一级缓存。

  2. 第二次查询 getUserById(1) 时,一级缓存命中,直接从缓存中返回结果,不会执行数据库查询。

  3. 执行 updateUser 操作后,MyBatis 会清空一级缓存,确保数据一致性。

  4. 第三次查询 getUserById(1) 时,一级缓存已被清空,MyBatis 会再次执行数据库查询。

注意事项:

  • 一级缓存是基于 SqlSession 的,因此在不同的 SqlSession 中,即使执行相同的查询,也会执行数据库查询。

  • 当执行更新、插入、删除操作时,MyBatis 会自动清空一级缓存,以保证数据一致性。

  • 一级缓存无法手动配置失效策略,其生命周期与 SqlSession 一致。

3.1.3 二级缓存 (Global Cache)

二级缓存是 Mapper 级别的缓存,多个 SqlSession 可以共享。 需要手动开启和配置。

工作原理:

  1. 当 SqlSession 执行查询操作时,MyBatis 会先检查二级缓存中是否存在对应的缓存数据。

  2. 如果缓存命中,则直接从缓存中返回结果,避免数据库查询。

  3. 如果缓存未命中,则执行数据库查询,并将查询结果存入二级缓存。

  4. 当 Mapper 中执行更新、插入、删除操作时,会清空该 Mapper 对应的二级缓存。

特点:

  • Mapper 级别: 多个 SqlSession 可以共享同一个 Mapper 的二级缓存。

  • 手动开启和配置: 需要在 mybatis-config.xml 和 Mapper XML 文件中进行配置。

  • 生命周期长: 比一级缓存更长,可以跨 SqlSession 存在。

  • 可配置: 可以配置缓存的失效策略、大小等。

配置步骤:

  1. mybatis-config.xml 中开启二级缓存:

    <configuration> <settings> <setting name="cacheEnabled" value="true"/> </settings> <!-- 其他配置 --> </configuration>
  2. 在 Mapper XML 文件中配置 <cache> 元素:

    <!-- UserMapper.xml --> <mapper namespace="com.example.mapper.UserMapper"> <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/> <select id="getUserById" resultType="com.example.entity.User"> SELECT * FROM user WHERE id = #{id} </select> <update id="updateUser" parameterType="com.example.entity.User"> UPDATE user SET name = #{name} WHERE id = #{id} </update> </mapper>

    <cache> 元素属性说明:

    • eviction: 缓存回收策略,可选值包括:

      • LRU (Least Recently Used):最近最少使用,移除最长时间不被使用的对象。

      • FIFO (First In First Out):先进先出,按对象进入缓存的顺序移除。

      • SOFT: 软引用,移除基于垃圾回收器状态和软引用规则的对象。

      • WEAK: 弱引用,更积极地移除基于垃圾回收器状态和弱引用规则的对象。

    • flushInterval: 缓存刷新间隔,单位为毫秒。 默认为不刷新。

    • size: 缓存中存放对象的最大数量。 默认为 1024。

    • readOnly: 是否只读。 如果设置为 true,则所有从缓存中获取的对象都是只读的,不允许修改,可以提高并发性能。 如果设置为 false,则从缓存中获取的对象是可修改的,但会降低并发性能。

    • type: 自定义缓存实现类。

  3. 实体类需要实现 Serializable 接口:

    因为二级缓存需要将对象序列化到磁盘或网络中,所以实体类必须实现 Serializable 接口。

    import java.io.Serializable; public class User implements Serializable { private static final long serialVersionUID = 1L; // 属性 }

代码实践:

// 获取 SqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 第一次 SqlSession try (SqlSession sqlSession1 = sqlSessionFactory.openSession()) { UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class); // 第一次查询,会执行数据库查询,并将结果放入二级缓存 User user1 = userMapper1.getUserById(1); System.out.println("First query (SqlSession 1): " + user1); } // 第二次 SqlSession try (SqlSession sqlSession2 = sqlSessionFactory.openSession()) { UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class); // 第二次查询,直接从二级缓存获取 User user2 = userMapper2.getUserById(1); System.out.println("Second query (SqlSession 2): " + user2); } // 第三次 SqlSession try (SqlSession sqlSession3 = sqlSessionFactory.openSession()) { UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class); // 执行更新操作,清空二级缓存 User user = userMapper3.getUserById(1); user.setName("Updated Name"); userMapper3.updateUser(user); sqlSession3.commit(); // 第四次查询,会执行数据库查询 User user3 = userMapper3.getUserById(1); System.out.println("Third query (SqlSession 3): " + user3); }

代码解释:

  1. 第一次查询 getUserById(1) 时,二级缓存为空,MyBatis 会执行数据库查询,并将结果放入二级缓存。

  2. 第二次查询 getUserById(1) 时,虽然在不同的 SqlSession 中,但二级缓存命中,直接从缓存中返回结果,不会执行数据库查询。

  3. 执行 updateUser 操作后,MyBatis 会清空 UserMapper 对应的二级缓存。

  4. 第四次查询 getUserById(1) 时,二级缓存已被清空,MyBatis 会再次执行数据库查询。

注意事项:

  • 需要在 mybatis-config.xml 和 Mapper XML 文件中进行配置才能开启二级缓存。

  • 实体类必须实现 Serializable 接口。

  • 当 Mapper 中执行更新、插入、删除操作时,会清空该 Mapper 对应的二级缓存。

  • 可以使用 <cache-ref> 元素引用其他 Mapper 的缓存配置。

缓存流程图:

3.1.4 缓存失效策略

缓存失效策略至关重要,直接影响缓存的有效性和数据一致性。

  • 基于时间的失效: 设置缓存的过期时间,超过时间后缓存失效。

  • 基于容量的失效: 设置缓存的最大容量,超过容量后根据一定的算法(如 LRU、FIFO)移除缓存数据。

  • 基于事件的失效: 当数据发生变更时,手动或自动清空缓存。

MyBatis 提供了 flushInterval 属性来配置基于时间的失效,以及 eviction 属性配置基于容量的失效策略。 基于事件的失效可以通过在更新、插入、删除操作后手动清空缓存来实现,也可以通过配置 MyBatis 的拦截器来实现自动清空缓存。

3.1.5 总结

MyBatis 的缓存机制可以显著提高查询性能,减少数据库访问次数。 一级缓存是 SqlSession 级别的缓存,默认开启,生命周期短。 二级缓存是 Mapper 级别的缓存,需要手动开启和配置,多个 SqlSession 可以共享。 选择合适的缓存策略,并合理配置缓存失效策略,是使用 MyBatis 缓存的关键。

选择缓存的建议:

  • 对于频繁访问且数据变化不频繁的数据,可以使用二级缓存。

  • 对于需要保证数据实时性的数据,不建议使用缓存。

  • 对于只读数据,可以将 readOnly 属性设置为 true,提高并发性能。

  • 根据实际情况选择合适的缓存回收策略和刷新间隔。

  • 在更新、插入、删除操作后,及时清空相关缓存,保证数据一致性。

希望本文能够帮助你更好地理解和使用 MyBatis 的缓存机制。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U