MyBatis 学习(七)之 缓存

目录

[1 MyBatis 缓存介绍](#1 MyBatis 缓存介绍)

[2 一级缓存](#2 一级缓存)

[3 二级缓存](#3 二级缓存)

[3.1 二级缓存介绍](#3.1 二级缓存介绍)

[3.2 二级缓存配置](#3.2 二级缓存配置)

[3.3 二级缓存测试](#3.3 二级缓存测试)

[4 参考文档](#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 时,一级缓存仅对当前执行的语句有效。这意味着每次语句执行完毕后,缓存就会被清空。这种设置适用于那些可能需要避免脏读风险的场景,因为它确保了每次查询都是独立的,不会受到其他查询的影响
java 复制代码
@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 配置文件进行设置

XML 复制代码
<!--开启二级缓存-->
<settings>
    <setting name="cacheEnabled" value="true"/>
</settings>

在 SQL 映射文件中设置 <cache> 标签

在 <cache> 标签中,可以设置多种属性来定义缓存的行为,例如:

  • eviction:指定缓存回收策略,默认是 LRU(最近最少使用)
    • LRU:最近最少使用:移除最长时间不被使用的对象
    • FIFO:先进先出:按对象进入缓存的顺序来移除它们
    • SOFT:软引用:移除基于垃圾回收器状态和软引用规则的对象
    • WEAK:弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象
  • flushInterval:设置缓存刷新间隔,单位为毫秒,默认不清空
  • size:指定二级缓存可以存储的最大对象数量,默认是 1024
  • readOnly:标记缓存是否为只读,默认为 false,即可读可写,这回慢一点,但是安全
  • type:指定缓存的具体实现类型
XML 复制代码
<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>

注:在 Mapper 接口使用 @CacheNamespace 注解也可以开启二级缓存

实体类实现 java.io.Serializable 接口

3.3 二级缓存测试

java 复制代码
@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)

相关推荐
Hello.Reader3 小时前
Redis 延迟监控深度指南
数据库·redis·缓存
DKPT5 小时前
Java桥接模式实现方式与测试方法
java·笔记·学习·设计模式·桥接模式
DuelCode7 小时前
Windows VMWare Centos Docker部署Springboot 应用实现文件上传返回文件http链接
java·spring boot·mysql·nginx·docker·centos·mybatis
好好研究7 小时前
学习栈和队列的插入和删除操作
数据结构·学习
幽络源小助理8 小时前
SpringBoot基于Mysql的商业辅助决策系统设计与实现
java·vue.js·spring boot·后端·mysql·spring
Hello.Reader8 小时前
Redis 延迟排查与优化全攻略
数据库·redis·缓存
新中地GIS开发老师8 小时前
新发布:26考研院校和专业大纲
学习·考研·arcgis·大学生·遥感·gis开发·地理信息科学
SH11HF9 小时前
小菜狗的云计算之旅,学习了解rsync+sersync实现数据实时同步(详细操作步骤)
学习·云计算
Frank学习路上9 小时前
【IOS】XCode创建firstapp并运行(成为IOS开发者)
开发语言·学习·ios·cocoa·xcode
荔枝吻9 小时前
【沉浸式解决问题】idea开发中mapper类中突然找不到对应实体类
java·intellij-idea·mybatis