Mybatis 缓存机制
MyBatis 缓存是用来减少数据库查询次数,提高性能的一种机制。MyBatis 提供了两级缓存:一级缓存和二级缓存。
1. 一级缓存(Session级缓存)
-
作用范围:一级缓存是 MyBatis 默认开启的缓存,作用于一个 SqlSession 内部,查询结果会缓存到 SqlSession 的本地缓存中,只要在同一个 SqlSession 中发起相同的查询,MyBatis 会直接返回缓存中的结果,而不会重新查询数据库。
-
特点:
- 仅限当前 SqlSession 生命周期内有效。
- 会话关闭时,一级缓存会被清除。
- 如果在同一个 SqlSession 内执行相同的查询,MyBatis 会直接从缓存中获取结果,避免了重复查询。
-
注意事项:
- 如果执行了
clearCache()方法或者手动关闭了 SqlSession,缓存会被清空。 - 通过
flushStatements()方法也会清空缓存。
- 如果执行了
因为现在开发不会手动获取 sqlSession 来执行查询,都是基于 mapper 接口来查询的,所以简单点可以这样理解:
如果一个方法使用了事物注解,这个方法里查询数据库的数据会被缓存起来,如果再执行相同的查询会到缓存中取数据。如果这个方法没有使用事物注解,一级缓存就不会生效了
@Transactional
public void find(){
// 第一次查询:到数据库查询并缓存起来(事物未结束,sqlSession 就不会关闭,一级缓存基于 sqlSession)
userMapper.findById(1L);
// 第二次查询:直接查缓存,不会再发 sql 了
userMapper.findById(1L);
}
// @Transactional 如果这个方法没有加事物,两个方法都会发sql到数据库查询(因为每个查询都会打开一个新的 sqlSession)
public void find(){
userMapper.findById(1L);
userMapper.findById(1L);
}
@Transactional
public void find(){
// 第一次查询,会到数据库查询
userMapper.findById(1L);
// 删除 7 号(只要同一个sqlSession 中执行了增删改,一级缓存会失效,哪怕修改的同一条数据)
userMapper.deleteById(7L);
// 第二次查询,也会到数据库查询
userMapper.findById(1L);
}
2. 二级缓存(Mapper级缓存)
-
作用范围:二级缓存是跨 SqlSession 共享的缓存,作用于不同 SqlSession 之间的缓存。二级缓存由 SqlSessionFactory 管理,不会随着 SqlSession 的关闭而清空,缓存的生命周期长于 SqlSession。
-
开启二级缓存:
-
在 MyBatis 配置文件中开启二级缓存:
<settings> <setting name="cacheEnabled" value="true"/> </settings> -
在对应的 Mapper 文件中,使用
<cache>标签启用:<mapper namespace="com.example.Mapper"> <cache/> </mapper>
-
-
特点:
- 存储在内存中,多个 SqlSession 可以共享。
- 当不同的 SqlSession 查询相同的数据时,可以直接从二级缓存中获取。
- 只要缓存有效,查询时会先尝试从二级缓存中获取数据,若缓存中没有则查询数据库并将结果存入缓存。
-
缓存的清除:
- 二级缓存的清除机制较为复杂,一般会在以下几种情况下清除:
- 在执行增、删、改操作时,MyBatis 会清除相关 Mapper 的缓存。
- 可以通过配置
flushInterval来指定缓存的刷新间隔。
- 二级缓存的清除机制较为复杂,一般会在以下几种情况下清除:
-
缓存存储方式:
- MyBatis 默认使用
PerpetualCache(一个基于 HashMap 的缓存实现)来存储数据。 - 你也可以自定义缓存实现,例如使用 Ehcache 或 Redis 等外部缓存系统。
- MyBatis 默认使用
3. 缓存的刷新策略
-
缓存的更新:MyBatis 会自动对更新操作(insert, update, delete)进行缓存的刷新。对于查询操作,缓存会在查询时根据需要进行刷新。
-
缓存的失效:
- 如果你修改了数据库中的数据,而不通过 MyBatis 来执行修改操作,那么缓存就会失效,因此最好通过 MyBatis 提供的增、删、改操作来确保缓存的一致性。
4. 缓存的配置示例
<mapper namespace="com.example.Mapper">
<cache/>
<select id="selectUser" resultType="com.example.User">
SELECT * FROM users WHERE id = #{id}
</select>
<insert id="insertUser">
INSERT INTO users (name, age) VALUES (#{name}, #{age})
</insert>
</mapper>
在这个例子中,<cache/> 标签启用了二级缓存。
5. 使用缓存的最佳实践
-
一级缓存:
- 使用适当的 SqlSession 生命周期,不要频繁地创建和关闭 SqlSession。
- 在同一个事务中使用相同的 SqlSession 进行查询,以充分利用一级缓存。
-
二级缓存:
- 启用二级缓存时,确保业务操作的缓存一致性,及时刷新缓存。
- 对于高频率的查询,可以通过二级缓存减轻数据库的负担。
- 使用外部缓存(如 Redis、Ehcache)时,要配置好缓存的过期时间和存储策略。
总结
- 一级缓存:是 MyBatis 默认的缓存,存在于 SqlSession 内部,生命周期与 SqlSession 相同。
- 二级缓存:是可配置的全局缓存,作用于多个 SqlSession,生命周期与 SqlSessionFactory 相同(基本不会用到,redis)。
- 缓存的合理使用可以显著提高 MyBatis 性能,但要注意缓存一致性和失效机制。

浙公网安备 33010602011771号