目录
1 MyBatis 缓存介绍
2 一级缓存
3 二级缓存
3.1 二级缓存介绍
3.2 二级缓存配置
3.3 二级缓存测试
4 参考文档
1 MyBatis 缓存介绍
MyBatis 缓存是 MyBatis 中的一个重要特性,用于提高数据库查询的性能。MyBatis 提供了一级缓存和二级缓存两种类型的缓存机制。
-
一级缓存:一级缓存(本地缓存)是 MyBatis 中默认的缓存机制,它是 SqlSession 级别的缓存。当使用 SqlSession 执行一次查询时,查询到的结果集会存储在 SqlSession 的缓存中。当使用 SqlSession 再次执行相同的查询时,它会首先从缓存中获取结果,而不会再次查询数据库。一级缓存是基于事务的,当 SqlSession flush(插入、更新、修改) 或 close 后,该 SqlSession 中的所有 Cache 将被清空
-
二级缓存:二级缓存是 Mapper 级别的缓存,也称为 namespace 级别。这意味着无论通过哪个 SqlSession 执行相同的 Mapper 接口操作,都可以访问到相同的二级缓存数据,即跨 SqlSession 共享同一 Mapper 接口的数据。二级缓存需要手动配置,它可以实现更大范围的数据共享和更高效的查询性能。二级缓存也可以配置为使用不同的缓存实现,如 Ehcache、Redis 等
MyBatis 缓存的主要作用是减少对数据库的访问次数,提高查询性能。然而,需要注意的是,缓存也会占用内存空间,如果缓存的数据量过大,可能会导致内存溢出的问题。因此,在使用 MyBatis 缓存时,需要根据具体的应用场景和数据量进行合理的配置和调优。
2 一级缓存
一级缓存 (local cache),即本地缓存,作用域默认为 SqlSession 。本地缓存不能被关闭,当 SqlSession flush(插入、更新、修改) 或 close 后,该 SqlSession 中的所有 Cache 将被清空。此外,也可以调用 sqlSession.clearCache() 来手动清空本地缓存。
在 MyBatis 配置文件中可以通过设置 localCacheScope 参数来改变一级缓存的作用域,它有以下两种设置:
- SESSION: 当设置为 SESSION 时,一级缓存将对整个 SqlSession 有效。这意味着在同一个 SqlSession 中执行的查询会共享同一个缓存。只有当执行 DML 语句(如插入、更新或删除操作)时,缓存才会被清除。这可以有效地提高相同数据多次查询的速度,但如果在多个 SqlSession 之间需要同步数据,或者有写操作发生,可能会出现脏读的情况
- STATEMENT:当设置为 STATEMENT 时,一级缓存仅对当前执行的语句有效。这意味着每次语句执行完毕后,缓存就会被清空。这种设置适用于那些可能需要避免脏读风险的场景,因为它确保了每次查询都是独立的,不会受到其他查询的影响
@Test
public void selectById1() {EmployeeMapper mapper = sqlSessionOne.getMapper(EmployeeMapper.class);// 第一次查询System.out.println("第一次查询");Employee employee1 = mapper.selectEmployeeById(1);System.out.println(employee1);// 第二次查询System.out.println("第二次查询");Employee employee2 = mapper.selectEmployeeById(1);System.out.println(employee2);// 手动清除本地缓存System.out.println("清除本地缓存");sqlSessionOne.clearCache();// 第三次查询System.out.println("第三次查询");Employee employee3 = mapper.selectEmployeeById(1);System.out.println(employee3);}
第一次查询,从数据库中查询数据并将查询结果保存在本地缓存中
第二次查询,直接获取本地缓存的查询结果
清空本地缓存
第三次查询,从数据库中查询数据
3 二级缓存
3.1 二级缓存介绍
MyBatis 的二级缓存是一个更为全局的缓存机制,其作用范围是 namespace 级别,可以被多个 SqlSession 共享。它的生命周期与应用程序同步,主要用于解决一级缓存不能跨会话共享的问题。
以下是二级缓存的一些详细介绍:
- 使用场景:二级缓存适用于多个 SqlSession 需要共享数据的场景。当开启二级缓存后,MyBatis 会先从二级缓存中获取数据,如果没有找到,则会继续查找一级缓存,最后才访问数据库
- 配置条件:要使用二级缓存,需要满足以下条件:
- 全局性地开启或关闭所有映射器配置文件中已配置的任何二级缓存,可以在 MyBatis 配置文件中通过设置 <setting name="cacheEnabled" value="true"> 来实现
- 在需要使用二级缓存的映射器配置文件(如 sqlMapper.xml)中添加 <cache/> 配置
- 使用二级缓存的实体类对象必须是可序列化的,即实现 java.io.Serializable 接口
- 工作机制:当 SqlSession 对象关闭或提交之后,一级缓存中的数据会被写入到二级缓存当中。这样,即使在不同的 SqlSession 之间,也可以访问到相同的数据集,从而提高了数据访问的效率
- 缓存策略:二级缓存的回收策略可以通过设置 <cache> 标签的属性来定义,例如 LRU(最近最少使用)策略,用于移除最长时间不被使用的对象
3.2 二级缓存配置
在 MyBatis 配置文件进行设置
<!--开启二级缓存-->
<settings><setting name="cacheEnabled" value="true"/>
</settings>
在 SQL 映射文件中设置 <cache> 标签
在 <cache> 标签中,可以设置多种属性来定义缓存的行为,例如:
- eviction:指定缓存回收策略,默认是 LRU(最近最少使用)
- LRU:最近最少使用:移除最长时间不被使用的对象
- FIFO:先进先出:按对象进入缓存的顺序来移除它们
- SOFT:软引用:移除基于垃圾回收器状态和软引用规则的对象
- WEAK:弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象
- flushInterval:设置缓存刷新间隔,单位为毫秒,默认不清空
- size:指定二级缓存可以存储的最大对象数量,默认是 1024
- readOnly:标记缓存是否为只读,默认为 false,即可读可写,这回慢一点,但是安全
- type:指定缓存的具体实现类型
<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>
注:在 Mapper 接口使用 @CacheNamespace 注解也可以开启二级缓存
实体类实现 java.io.Serializable 接口
3.3 二级缓存测试
@Test
public void selectAll() {// 不同 sqlSession 对同一 mapper 接口进行操作SqlSession sqlSession1 = sqlSessionFactory.openSession();EmployeeMapper mapper1 = sqlSession1.getMapper(EmployeeMapper.class);SqlSession sqlSession2 = sqlSessionFactory.openSession();EmployeeMapper mapper2 = sqlSession2.getMapper(EmployeeMapper.class);// 通过 sqlSession1 进行第一次查询List<Employee> employees1 = mapper1.selectAllEmployee();for (Employee employee : employees1) {System.out.println(employee);}// 当 sqlsession 提交、关闭时,二级缓存会有数据//sqlSession1.commit();sqlSession1.clearCache();sqlSession1.close();// 通过 sqlSession2 进行第二次查询List<Employee> employees2 = mapper2.selectAllEmployee();for (Employee employee : employees2) {System.out.println(employee);}
}
通过 sqlSession1 进行第一次查询,二级缓存没有数据
清空 sqlSession1 的一级缓存
sqlSession1 关闭(二级缓存有查询结果,二级缓存的数据是从一级缓存中来的吗???)
通过 sqlSession2 进行第二次查询,从二级缓存中获得查询结果,二级缓存命中率 0.5
补充:
禁用二级缓存
可以使用 useCache 属性(这个属性只有 select 有)来禁用二级缓存,默认为 true,即使用二级缓存,当然也可以通过 @Options(useCache=true) 来设置。
清空一级和二级缓存
flushCache 属性是用来清空一级缓存和二级缓存的。select 中默认为 flushCache=false,insert、delete、update 中默认为 flushCache=true,因为执行数据的增删改 SQL 语句后,数据库与缓存数据可能已经不一致,此时缓存已经不能用了,如果不清空缓存则可能出现脏读的情况。也可以通过 @Options(flushCache=options.FlushCachePolicy.TRUE) 进行设置。
也可以通过实现 cache 接口自定义二级缓存
4 参考文档
尚硅谷文档
mybatis 中文文档 – MyBatis 3 | XML 映射器
Mybatis3详解(十二)----Mybatis缓存 - 唐浩荣 - 博客园 (cnblogs.com)