当前位置: 首页 > article >正文

MyBatis 缓存机制详解

目录

一、什么是缓存?

1. 什么是缓存?

2. 为什么使用缓存?

3. 什么样的数据适合使用缓存?

二、MyBatis 缓存机制

1. 一级缓存(也叫本地缓存)

2. 一级缓存失效的情况

3. 二级缓存

4. 二级缓存失效的情况

三、MyBatis 缓存查询顺序

四、MyBatis 缓存的高级配置


前言

        在高并发系统中,数据库查询是系统性能的瓶颈之一。为了提高查询效率,减少数据库的访问次数,缓存机制应运而生。MyBatis 作为一款优秀的 ORM 框架,内置了强大的查询缓存特性,支持一级缓存二级缓存两级缓存机制。本文将详细介绍缓存的概念、MyBatis 的缓存机制以及如何配置和使用 MyBatis 缓存。


一、什么是缓存?

1. 什么是缓存?

缓存是存储在内存中的数据。
当用户查询数据时,系统可以直接从缓存中获取,而不需要访问数据库,从而提升查询效率。

2. 为什么使用缓存?

  • 减少数据库交互,降低系统的开销。
  • 提高查询效率,优化系统性能。
  • 支持高并发,避免数据库成为系统瓶颈。

3. 什么样的数据适合使用缓存?

  • 经常被查询
  • 变化不频繁

对于频繁变更的数据(如订单状态),缓存并不适用,因为数据不一致的风险较高。


二、MyBatis 缓存机制

MyBatis 内置了强大的缓存机制,支持两级缓存

  • 一级缓存(本地缓存):默认开启,作用域为 SqlSession 级别
  • 二级缓存(全局缓存):需要手动开启,作用域为 SqlSessionFactory 级别

1. 一级缓存(也叫本地缓存)

         在使用一级缓存后,与数据库第一次会话(SqlSession )期间查询到的数据会被放入本地缓存当中。如果在同一个会话后续需要获取相同的数据,可以直接从缓存中获取,而没必要再去查询数据库

(1)一级缓存的特点
  • 默认开启,不需要额外配置。
  • 作用域是SqlSession 级别,即同一个 SqlSession 多次执行相同查询,结果会被缓存
  • 不同 SqlSession 之间缓存互不影响
(2)一级缓存示例
@Test
public void findById() throws IOException {
    // 加载配置文件
    InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
    
    // 创建 SqlSession
    SqlSession session = factory.openSession();
    UserDao mapper = session.getMapper(UserDao.class);

    // 第一次查询
    User user1 = mapper.findById(1);
    System.out.println(user1.toString());

    // 第二次查询(相同的查询)
    User user2 = mapper.findById(1);
    System.out.println(user2.toString());

    // 判断两次查询结果是否相同
    System.out.println(user1 == user2); // true,说明数据来自缓存

    session.close();
    in.close();
}

运行如下:

结论:同一个 SqlSession (会话)下,第一次查询的数据会缓存,第二次查询相同数据时直接从缓存中获取,不会执行 SQL 语句。


2. 一级缓存失效的情况

以下情况会导致 MyBatis 一级缓存失效:

(1).不同 SqlSession

如果两次查询使用的是不同的 SqlSession,则缓存不会共享,查询会直接访问数据库。

(2).查询条件不同
因为查询条件不同,缓存未命中。

mapper.findById(1);
mapper.findById(2);

(3).执行了增删改操作

任何增删改操作都会清空 SqlSession 的缓存,以确保数据一致性。

mapper.findById(1);
mapper.updateUser(user);
mapper.findById(1);

 

(4).手动清除缓存
手动清除一级缓存后,查询将重新访问数据库。

session.clearCache();


3. 二级缓存

(1)二级缓存的特点
  • 默认关闭,需要手动开启。
  • 作用域是SqlSessionFactory 级别,即同一 SqlSessionFactory 产生的多个 SqlSession 共享缓存
  • 缓存的数据是对象的副本,而不是对象本身(即缓存的是数据,不是对象实例)。
(2)开启二级缓存的条件
  1. 在核心配置文件(SqlMapConfig.xml)中开启全局缓存

    <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>
    
  2. 在对应的 Mapper 配置文件中声明使用二级缓存

    <!--使用二级缓存-->
    <cache/>
    
  3. 实体类必须实现 Serializable 接口

    public class User implements Serializable {
        private Integer id;
        private String username;
        private Date birthday;
        private String sex;
        private String address;
    }
    
  4. SqlSession 关闭或提交后,数据才会进入二级缓存

    @Test
    public void findById() throws IOException {
        // 1.加载SqlMapConfig配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.创建sqlSessionFactory工厂
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        //3.sqlSessionFactory创建sqlSession
        SqlSession sqlSession = factory.openSession();
        SqlSession sqlSession2 = factory.openSession();
    
        //4.通过Session创建UserDao接口代理对象
        UserDao mapper = sqlSession.getMapper(UserDao.class);
        User user1 = mapper.findById(1);
        System.out.println(user1.toString());
        // 将其一级缓存的数据放进二级缓存中,并清空一级缓存
        sqlSession.close();
    
    
        System.out.println("-----------------");
        UserDao mapper2 = sqlSession2.getMapper(UserDao.class);
        User user2 = mapper2.findById(1);
        System.out.println(user2.toString());
        // 将其一级缓存的数据放进二级缓存中,并清空一级缓存
        sqlSession2.close();
    
        System.out.println(user1 == user2);
        
        resourceAsStream.close();
    }
    

 运行结果:

 最后输出false,证明user1和user2并不是同一个对象;得出一下结论:

结论: 二级缓存存储的是数据,而不是对象,因此即使查询相同数据,返回的对象实例仍然不同。 


4. 二级缓存失效的情况

  • 执行了增删改操作:会清空一级缓存和二级缓存,以保证数据一致性。

三、MyBatis 缓存查询顺序

  1. 优先查询二级缓存
    因为二级缓存是 SqlSessionFactory 级别,多个 SqlSession 可能已经缓存了数据。
  2. 如果二级缓存未命中,则查询一级缓存
  3. 如果一级缓存也未命中,则查询数据库
  4. SqlSession 关闭后,一级缓存数据会写入二级缓存


四、MyBatis 缓存的高级配置

Mapper.xml 文件中,可以自定义缓存策略:

<cache eviction="FIFO"
       flushInterval="60000"
       size="512"
       readOnly="true"/>

Catch参数的具体细节说明:

eviction(收回策略)

  • LRU(最近最少使用的):移除最长时间不被使用的对象,这是默认值。
  • FIFO(先进先出):按对象进入缓存的顺序来移除它们。
  • SOFT(软引用):移除基于垃圾回收器状态和软引用规则的对象。
  • WEAK(弱引用):更积极地移除基于垃圾收集器状态和弱引用规则的对象。

flushinterval(刷新间隔)

  • 可以被设置为任意的正整数,单位是毫秒。
  • 默认情况不设置,即没有刷新间隔,缓存仅仅在调用语句时刷新。

size(缓存容量)

  • 最大缓存对象数,可以被设置为任意正整数,默认值是1024 。

readOnly(只读) 属性可以被设置为 true / false。

  • true只读缓存:所有调用者获取到的都是缓存对象本身,并且不能对其进行修改。 这提供了很重要的性能优势。
  • false读写缓存: 通过序列化返回缓存对象的拷贝版,这种方式会慢一些,但是安全,因此默认是 false。

五、总结

  • 一级缓存:默认开启,作用域是 SqlSession,缓存的是 查询结果SqlSession 关闭后失效
  • 二级缓存:默认关闭,作用域是 SqlSessionFactory,多个 SqlSession 共享,需手动开启,SqlSession 关闭后数据进入二级缓存
  • 缓存查询顺序:二级缓存 → 一级缓存 → 数据库。
  • 缓存失效情况:SqlSession 关闭、执行增删改操作、手动清除缓存等。

合理使用 MyBatis 缓存,可以有效提高查询性能,减少数据库访问压力,提高系统并发能力。


http://www.kler.cn/a/525015.html

相关文章:

  • Linux——网络(tcp)
  • SVG 矩形:深入理解与实际应用
  • Julia 之 @btime 精准测量详解
  • Unbutu虚拟机+eclipse+CDT编译调试环境搭建
  • python学opencv|读取图像(四十九)使用cv2.bitwise()系列函数实现图像按位运算
  • 2023年吉林省职业院校技能大赛网络系统管理样题-网络配置(华三代码)
  • Java学习教程,从入门到精通,JDBC 删除表语法及案例(103)
  • 基于Langchain-Chatchat + ChatGLM 本地部署知识库
  • 240. 搜索二维矩阵||
  • 【JavaEE】Spring(6):Mybatis(下)
  • docker安装emqx
  • 11JavaWeb——SpringBootWeb案例02
  • S4 HANA明确Tax Base Amount是否考虑现金折扣(OB69)
  • 蓝桥杯python语言基础(4)——基础数据结构(下)
  • 洛谷P11464 支配剧场
  • 深度学习框架应用开发:基于 TensorFlow 的函数求导分析
  • SpringSecurity:There is no PasswordEncoder mapped for the id “null“
  • Kotlin 委托详解
  • 新项目传到git步骤
  • 【Redis】 String 类型的介绍和常用命令
  • 大模型应用的10个架构挑战
  • 嵌入式知识点总结 Linux驱动 (二)-uboot bootloader
  • SQLServer 不允许保存更改(主键)
  • 如何写一篇高质量的提示词?
  • AI驱动内容跨媒体转换新机遇
  • 护眼好帮手:Windows显示器调节工具