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)

相关推荐
凌冰_2 分钟前
IDEA2023 SpringBoot整合MyBatis(三)
spring boot·后端·mybatis
Natural_yz24 分钟前
大数据学习17之Spark-Core
大数据·学习·spark
qq_1728055931 分钟前
RUST学习教程-安装教程
开发语言·学习·rust·安装
一只小小汤圆1 小时前
opencascade源码学习之BRepOffsetAPI包 -BRepOffsetAPI_DraftAngle
c++·学习·opencascade
虾球xz1 小时前
游戏引擎学习第20天
前端·学习·游戏引擎
草莓base1 小时前
【手写一个spring】spring源码的简单实现--bean对象的创建
java·spring·rpc
LateBloomer7771 小时前
FreeRTOS——信号量
笔记·stm32·学习·freertos
legend_jz1 小时前
【Linux】线程控制
linux·服务器·开发语言·c++·笔记·学习·学习方法
Komorebi.py1 小时前
【Linux】-学习笔记04
linux·笔记·学习
乌啼霜满天2491 小时前
Spring 与 Spring MVC 与 Spring Boot三者之间的区别与联系
java·spring boot·spring·mvc