MyBatis 的 一级缓存 和 二级缓存 是用来提高查询性能的缓存机制,减少数据库的访问压力。它们分别作用在不同的作用域内,缓存的级别和生存周期也有所不同。下面我将分别详细介绍一级缓存和二级缓存。
1. 一级缓存(一级缓存)
作用域:
- 一级缓存的作用域是 SqlSession。每个
SqlSession
都有自己的一级缓存。一级缓存是 MyBatis 中默认启用的缓存,它是自动开启的,并且对于同一个SqlSession
,缓存数据会一直存在,直到SqlSession
被关闭或提交。
工作原理:
- 当你通过
SqlSession
执行查询时,查询结果会首先被存放到一级缓存中。一级缓存的生命周期是SqlSession
的生命周期,所以对于同一个SqlSession
中的多次查询,如果查询条件相同且数据没有发生改变,那么 MyBatis 会直接从缓存中获取查询结果,而不会再次访问数据库。 - 缓存命中:如果你查询同样的数据,MyBatis 会直接从一级缓存中获取结果,避免了数据库的多次查询。
- 缓存失效:当你调用
SqlSession
的clearCache()
方法或SqlSession
关闭时,一级缓存会被清空。
特点:
- 默认开启:一级缓存是 MyBatis 默认启用的缓存。
- 会话级缓存:一级缓存的作用域是
SqlSession
,同一个SqlSession
内的查询会共享缓存。 - 会话结束清空:
SqlSession
关闭或提交时,一级缓存会被清空。
示例:
SqlSession sqlSession = sqlSessionFactory.openSession();
User user1 = sqlSession.selectOne("selectUser", 1); // 第一次查询数据库
User user2 = sqlSession.selectOne("selectUser", 1); // 第二次查询,使用一级缓存,避免数据库查询
System.out.println(user1 == user2); // 输出true,表示从一级缓存获取
sqlSession.close(); // 关闭SqlSession后,一级缓存清空
2. 二级缓存(第二级缓存)
作用域:
- 二级缓存是 SqlSessionFactory 级别的缓存。在 MyBatis 中,二级缓存作用于整个
SqlSessionFactory
,它跨SqlSession
,也就是说,二级缓存可以在多个SqlSession
之间共享。因此,它适用于多个数据库连接之间的缓存共享,可以减少对数据库的查询压力。
工作原理:
-
二级缓存的开启:二级缓存默认是关闭的,需要通过配置文件或者注解显式开启。
-
缓存行为:当你在执行 SQL 查询时,首先会检查一级缓存。如果一级缓存没有命中,则会检查二级缓存。二级缓存的查找顺序:
- 查找一级缓存。
- 如果一级缓存中没有,再查找二级缓存。
- 如果二级缓存中也没有,才会发起数据库查询。
-
缓存存储:在查询结果没有命中的情况下,MyBatis 会将查询结果存入二级缓存。存入缓存的条件有:
- 查询结果不为空。
- 查询语句的参数及查询结果可序列化(一般情况下,MyBatis 会自动处理)。
-
缓存失效:二级缓存的失效机制相对复杂,通常是与数据的更新操作相关。例如,当数据发生变更时,二级缓存会被清除。
开启二级缓存:
为了启用二级缓存,需要在 MyBatis 配置文件中进行设置。
- 在
mybatis-config.xml
配置文件中开启二级缓存:
<configuration>
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
<mappers>
<mapper resource="UserMapper.xml"/>
</mappers>
</configuration>
- 在映射文件(
Mapper.xml
)中启用缓存:
在每个 mapper
文件中,需要显式声明开启缓存:
<mapper namespace="com.example.UserMapper">
<cache/>
<select id="selectUser" resultType="User">
SELECT * FROM users WHERE id = #{id}
</select>
</mapper>
二级缓存存储与清理:
- 存储方式:二级缓存可以存储在内存、文件、数据库等多种方式,具体存储的方式由自定义缓存实现。
- 清理机制:当执行
INSERT
、UPDATE
或DELETE
操作时,MyBatis 会自动清空相应namespace
中的缓存数据,防止脏数据被读取。
示例:
// 第一个 SqlSession 查询,查找二级缓存
SqlSession sqlSession1 = sqlSessionFactory.openSession();
User user1 = sqlSession1.selectOne("selectUser", 1); // 查询并缓存
sqlSession1.close(); // 关闭SqlSession,缓存数据仍然存在于二级缓存
// 第二个 SqlSession 查询,查找二级缓存
SqlSession sqlSession2 = sqlSessionFactory.openSession();
User user2 = sqlSession2.selectOne("selectUser", 1); // 从二级缓存获取
sqlSession2.close();
System.out.println(user1 == user2); // 输出true,表示从二级缓存获取
3. 一级缓存与二级缓存的区别:
特性 | 一级缓存(Session 缓存) | 二级缓存(全局缓存) |
---|---|---|
缓存范围 | 单个 SqlSession 内部 | 整个 SqlSessionFactory 内部 |
默认开启 | 是 | 需要手动配置开启 |
存储数据的方式 | 存储在 SqlSession 中 | 存储在外部(如内存、磁盘等) |
缓存的生命周期 | SqlSession 生命周期内有效 | 直到 SqlSessionFactory 被销毁 |
跨 SqlSession | 不支持跨 SqlSession | 支持跨 SqlSession |
缓存的清空机制 | SqlSession 提交或关闭时清空 | 执行增、删、改操作时清空缓存 |
4. 总结:
- 一级缓存是 SqlSession 级别的缓存,默认开启,缓存的数据仅在同一个
SqlSession
中有效。 - 二级缓存是 SqlSessionFactory 级别的缓存,需要显式配置,跨多个
SqlSession
,能有效减少对数据库的访问。 - 在实际使用中,一级缓存和二级缓存是可以结合使用的。一级缓存通常用于短期内的数据缓存,而二级缓存则用于跨会话的长期缓存。