在 MyBatis 中,一级缓存 和 二级缓存 是两种用于提高查询性能的缓存机制。它们通过缓存数据库查询结果,避免每次都从数据库读取数据,从而减少数据库的负载并提高应用性能。
1. 一级缓存(一级缓存)
一级缓存是 MyBatis 的 本地缓存,作用范围限于 SqlSession。每次通过同一个 SqlSession
执行查询时,查询结果会被缓存下来。如果在同一个 SqlSession
中执行相同的查询,MyBatis 会直接从缓存中返回结果,而不是重新发送 SQL 请求到数据库。
特点:
- 作用范围:限于当前
SqlSession
。 - 生命周期:
SqlSession
的生命周期内,一级缓存有效。如果SqlSession
被关闭,则缓存会被清除。 - 默认启用:一级缓存是 MyBatis 的默认行为,不需要额外配置。
- 存储:缓存存储在
SqlSession
内存中,基于HashMap
进行存储,查询条件作为键,查询结果作为值。
工作原理:
- 第一次执行查询时,MyBatis 会向数据库发送 SQL 查询,并将查询结果存储在一级缓存中。
- 如果在同一个
SqlSession
中再次执行相同的查询,MyBatis 会首先检查一级缓存中是否已经有结果。如果有,直接从缓存中返回结果,不再查询数据库。
示例:一级缓存的工作原理
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 第一次查询,执行数据库操作
User user1 = userMapper.selectUserById(1);
// 第二次查询,结果将从一级缓存中获取,而不执行数据库操作
User user2 = userMapper.selectUserById(1);
sqlSession.close();
在上述代码中,第一次执行查询时,查询结果会被存储在一级缓存中。当第二次执行相同查询时,结果会直接从缓存中返回,避免了再次查询数据库。
清空一级缓存
- 当调用
sqlSession.clearCache()
时,会清空一级缓存。 - 当
SqlSession
被关闭时,一级缓存也会被清空。
2. 二级缓存(二级缓存)
二级缓存是 MyBatis 提供的 跨 SqlSession
的缓存机制,它的作用范围是 整个 Mapper 或 整个 SqlSessionFactory。不同的 SqlSession
可以共享二级缓存,从而有效地减少数据库的查询次数。
特点:
- 作用范围:跨
SqlSession
。 - 生命周期:当
SqlSessionFactory
被销毁时,二级缓存才会失效。它的生命周期比一级缓存长。 - 默认禁用:二级缓存默认是关闭的,需要在配置文件中显式启用。
- 存储:二级缓存可以配置使用不同的缓存存储机制(如内存缓存、外部缓存系统等),并且通常使用缓存框架(如 EHCache、Redis 等)来实现。
启用二级缓存
要启用二级缓存,首先需要在 MyBatis 的配置文件 mybatis-config.xml
中启用二级缓存,并在对应的 Mapper 文件中配置缓存。
配置 mybatis-config.xml
启用二级缓存
<configuration>
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
</configuration>
cacheEnabled
设置为 true
以启用二级缓存。
配置 Mapper 文件启用缓存
在每个需要使用二级缓存的 Mapper 文件中,使用 <cache>
标签来启用缓存。
<mapper namespace="com.example.mapper.UserMapper">
<cache eviction="LRU" flushInterval="60000" size="512" readOnly="false" />
<select id="selectUserById" resultType="com.example.model.User">
SELECT * FROM users WHERE id = #{id}
</select>
</mapper>
在上面的 Mapper 配置中:
<cache>
标签启用了二级缓存,并配置了缓存的一些属性,如:eviction
:指定缓存的淘汰策略,如 LRU(Least Recently Used)策略。flushInterval
:指定刷新缓存的时间间隔(以毫秒为单位)。size
:指定缓存的最大容量。readOnly
:是否设置为只读缓存,如果为true
,表示缓存中的数据不可修改。
工作原理:
- 当执行查询时,MyBatis 会首先检查二级缓存中是否有该查询的结果。如果存在,直接从缓存中读取结果。
- 如果二级缓存中没有结果,MyBatis 会执行数据库查询,将查询结果存储到二级缓存中,并返回给客户端。
- 在后续的
SqlSession
中,若再次执行相同的查询,将从二级缓存中获取结果,而不再查询数据库。
示例:二级缓存的工作原理
SqlSession sqlSession1 = sqlSessionFactory.openSession();
UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
// 第一次查询,查询结果存储到二级缓存
User user1 = userMapper1.selectUserById(1);
sqlSession1.close(); // 关闭 sqlSession1
SqlSession sqlSession2 = sqlSessionFactory.openSession();
UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
// 第二次查询,结果会从二级缓存中获取
User user2 = userMapper2.selectUserById(1);
sqlSession2.close(); // 关闭 sqlSession2
在这个例子中:
- 第一次查询时,结果会存储在二级缓存中。
- 即使使用不同的
SqlSession
(sqlSession1
和sqlSession2
),如果执行相同的查询,MyBatis 会从二级缓存中返回结果,而不再查询数据库。
3. 二级缓存的失效条件
二级缓存会在以下情况下失效:
- 数据库中相关数据发生变化(如更新、删除、插入等操作)。
- 调用
clearCache()
方法或commit()
、rollback()
时,会清空二级缓存。 - 二级缓存配置了超时时间,当缓存数据过期时,缓存会被清空。
4. 一级缓存与二级缓存的区别总结
特性 | 一级缓存(一级缓存) | 二级缓存(二级缓存) |
---|---|---|
作用范围 | 当前 SqlSession 内部 | 跨 SqlSession ,通常在 SqlSessionFactory 范围内 |
生命周期 | SqlSession 生命周期内有效,关闭时清空 | 只要 SqlSessionFactory 存在,就有效 |
默认开启 | 默认启用 | 默认关闭,需显式配置启用 |
存储方式 | 存储在 SqlSession 内存中 | 可以使用内存或外部缓存存储(如 Redis、EHCache 等) |
清除缓存 | SqlSession.close() 会清除一级缓存 | 需要清除特定的二级缓存或进行 flush 操作 |
性能 | 更快,通常用于在单一会话中重用数据 | 适用于跨会话的缓存,减少数据库查询次数 |
5. 总结
- 一级缓存:默认启用,作用范围限于当前
SqlSession
,缓存的内容只会在同一个SqlSession
内部重用。适用于局部的缓存需求,生命周期较短。 - 二级缓存:需要显式配置,作用范围是整个
SqlSessionFactory
,可以跨多个SqlSession
重用缓存数据,适用于跨会话的缓存场景,能够有效提高性能,尤其是在读取量较大的应用中。