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

Mybatis中的一级二级缓存扫盲

 思维导图:

 

  MyBatis 提供了一级缓存和二级缓存机制,用于提高数据库查询的性能,减少对数据库的访问次数。(本质上是减少IO次数)。


一级缓存


1. 概念

  一级缓存也称为会话缓存,它是基于 SqlSession 的缓存。在同一个 SqlSession 中,执行相同的 SQL 查询时,MyBatis 会优先从一级缓存中获取结果,而不是再次访问数据库。

2. 工作原理

2.1缓存结构:

  在 SqlSession 内部,一级缓存是一个 PerpetualCache 对象,它本质上是一个 HashMap,键是根据查询的 SQL 语句、参数、环境等信息生成的唯一标识,值是查询结果。

  查询流程:当调用 SqlSession 的查询方法时,MyBatis 会先将查询的 SQL 语句、参数等信息组合成一个唯一的缓存键。然后在 PerpetualCache 这个 HashMap 中查找该键对应的值。如果找到了,就直接返回该值;如果没找到,就会执行 SQL 查询,将查询结果存入 PerpetualCache 中,下次再执行相同查询时就可以直接从缓存中获取结果。

2.2 缓存命中的条件

相同的 SqlSession:必须是在同一个 SqlSession 实例中执行相同的查询,一级缓存才会生效。
相同的 SQL 语句:查询的 SQL 语句必须完全相同,包括 SQL 中的参数占位符和参数值。
相同的环境:查询的环境(如数据库连接、事务等)也必须相同。

代码示例:

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.InputStream;public class FirstLevelCacheDetailExample {public static void main(String[] args) throws Exception {String resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);// 创建第一个 SqlSessiontry (SqlSession sqlSession1 = sqlSessionFactory.openSession()) {// 第一次查询User user1 = sqlSession1.selectOne("com.example.UserMapper.selectUserById", 1);System.out.println("第一次查询结果: " + user1);// 第二次查询,使用相同的 SqlSessionUser user2 = sqlSession1.selectOne("com.example.UserMapper.selectUserById", 1);System.out.println("第二次查询结果: " + user2);// 两次查询结果相同,说明使用了一级缓存System.out.println("两次查询结果是否相同: " + (user1 == user2));// 执行更新操作sqlSession1.update("com.example.UserMapper.updateUser", new User(1, "New Name"));// 第三次查询User user3 = sqlSession1.selectOne("com.example.UserMapper.selectUserById", 1);System.out.println("第三次查询结果: " + user3);// 由于执行了更新操作,一级缓存已清空,user3 是重新查询数据库得到的结果System.out.println("第一次查询结果和第三次查询结果是否相同: " + (user1 == user3));}}
}

  在上述代码中,前两次查询使用相同的 SqlSession 和相同的查询条件,所以第二次查询会从一级缓存中获取结果。而执行更新操作后,一级缓存被清空,第三次查询会重新访问数据库。

3. 缓存失效情况

  • 不同的 SqlSession:每个 SqlSession 都有自己独立的一级缓存,不同的 SqlSession 之间的缓存是不共享的。
  • 执行 insert、update、delete 操作:当在同一个 SqlSession 中执行这些操作时,会清空该 SqlSession 的一级缓存,以保证数据的一致

4. 优缺点

优点:

  • 提高性能:在同一个 SqlSession 中多次执行相同查询时,避免了重复的数据库访问,减少了数据库的负载,提高了查询性能。
  • 简单易用:一级缓存是 MyBatis 内置的,无需额外配置,默认开启,使用方便。

缺点:

  • 作用范围小:只在同一个 SqlSession 中有效,不同的 SqlSession 之间无法共享缓存,限制了缓存的使用范围。
  • 数据一致性问题:如果在同一个 SqlSession 中执行了 insert、update、delete 操作,会清空该 SqlSession 的一级缓存,但如果在不同的 SqlSession 中对同一数据进行了修改,一级缓存可能会返回旧数据,导致数据不一致。

二级缓存

1. 工作原理

缓存结构:

二级缓存也是基于 PerpetualCache 实现的,但它是基于 SqlSessionFactory 的。每个 Mapper 可以有自己独立的二级缓存,也可以多个 Mapper 共享同一个二级缓存。

查询流程:当一个 SqlSession 执行查询操作时,MyBatis 会先检查该 Mapper 对应的二级缓存中是否存在该查询结果。如果存在,则直接从二级缓存中获取结果;如果不存在,则执行 SQL 查询,并将查询结果存入二级缓存中。在多个 SqlSession 之间,只要它们是由同一个 SqlSessionFactory 创建的,就可以共享二级缓存。

2. 配置详解

全局配置:在 mybatis-config.xml 中开启二级缓存的全局开关。

<settings><setting name="cacheEnabled" value="true"/>
</settings>

 Mapper 配置:在 Mapper 映射文件中配置缓存。

<mapper namespace="com.example.UserMapper"><!-- 开启二级缓存,并配置相关属性 --><cacheeviction="LRU" <!-- 缓存淘汰策略,这里使用最近最少使用策略 -->flushInterval="60000" <!-- 缓存刷新间隔,单位为毫秒 -->size="512" <!-- 缓存的最大对象数 -->readOnly="true" /> <!-- 是否只读 --><select id="selectUserById" resultType="com.example.User">SELECT * FROM users WHERE id = #{id}</select>
</mapper>

  • eviction:缓存淘汰策略,常见的有 LRU(最近最少使用)、FIFO(先进先出)等。
  • flushInterval:缓存刷新间隔,指定多长时间清空一次缓存。
  • size:缓存的最大对象数,当缓存中的对象数量超过该值时,会根据淘汰策略淘汰部分对象。
  • readOnly:是否只读。如果设置为 true,则缓存中的对象是只读的,MyBatis 会直接返回缓存中的对象,不会进行序列化和反序列化操作,性能较高;如果设置为 false,则每次返回缓存中的对象时都会进行序列化和反序列化操作,保证返回的对象是一个新的实例,但性能相对较低。

3. 示例代码及分析

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.InputStream;public class SecondLevelCacheDetailExample {public static void main(String[] args) throws Exception {String resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);// 第一个 SqlSessiontry (SqlSession sqlSession1 = sqlSessionFactory.openSession()) {User user1 = sqlSession1.selectOne("com.example.UserMapper.selectUserById", 1);System.out.println("第一个 SqlSession 查询结果: " + user1);}// 第二个 SqlSessiontry (SqlSession sqlSession2 = sqlSessionFactory.openSession()) {User user2 = sqlSession2.selectOne("com.example.UserMapper.selectUserById", 1);System.out.println("第二个 SqlSession 查询结果: " + user2);// 两次查询结果相同,说明使用了二级缓存System.out.println("两次查询结果是否相同: " + (user1 == user2));}// 执行更新操作try (SqlSession sqlSession3 = sqlSessionFactory.openSession()) {sqlSession3.update("com.example.UserMapper.updateUser", new User(1, "New Name"));sqlSession3.commit(); // 提交事务,清空二级缓存}// 第三个 SqlSessiontry (SqlSession sqlSession4 = sqlSessionFactory.openSession()) {User user3 = sqlSession4.selectOne("com.example.UserMapper.selectUserById", 1);System.out.println("第三个 SqlSession 查询结果: " + user3);// 由于执行了更新操作,二级缓存已清空,user3 是重新查询数据库得到的结果System.out.println("第一个查询结果和第三个查询结果是否相同: " + (user1 == user3));}}
}

 

  在上述代码中,第一个 SqlSession 执行查询后,结果会存入二级缓存。第二个 SqlSession 执行相同查询时,会从二级缓存中获取结果。执行更新操作并提交事务后,二级缓存会被清空,第三个 SqlSession 执行查询时会重新访问数据库。

3. 缓存失效情况

  • 执行 insert、update、delete 操作:当执行这些操作时,会清空该 Mapper 对应的二级缓存,以保证数据的一致性。
  • 缓存刷新策略:可以通过配置缓存的刷新策略,如 flushInterval 来定期清空缓存。

4. 优缺点

优点

  • 作用范围大:多个 SqlSession 可以共享二级缓存,减少了数据库的访问次数,提高了系统的整体性能。
  • 可配置性强:可以通过配置不同的缓存淘汰策略、刷新间隔等参数,满足不同的业务需求。

缺点

  • 配置复杂:需要在全局配置和 Mapper 映射文件中进行配置,相对一级缓存来说配置较为复杂。
  • 数据一致性问题:如果在不同的 Mapper 中对同一数据进行了修改,可能会导致二级缓存中的数据不一致,需要手动清空缓存或使用更复杂的缓存刷新策略。

5.一级缓存和二级缓存的比较

作用范围:一级缓存是基于 SqlSession 的,作用范围较小;二级缓存是基于 SqlSessionFactory 的,作用范围较大。

缓存共享:一级缓存不共享,每个 SqlSession 有自己独立的缓存;二级缓存可以在多个 SqlSession 之间共享。

开启方式:一级缓存默认开启;二级缓存需要手动配置开启。
通过合理使用一级缓存和二级缓存,可以有效提高 MyBatis 应用的性能。但在使用缓存时,需要注意数据的一致性问题,避免出现脏数据。

总结

  MyBatis 的一级缓存和二级缓存各有优缺点,在实际应用中需要根据具体的业务场景合理使用。一级缓存适用于在同一个 SqlSession 中多次执行相同查询的场景,而二级缓存适用于多个 SqlSession 之间共享缓存的场景。同时,需要注意缓存的使用可能会导致数据一致性问题,需要在业务逻辑中进行相应的处理。

    相关文章:

  • 一个读写excel的简单程序(golang)
  • IP 互联网协议
  • 数字智慧方案5867丨智慧建造(BIM技术智慧工地)在施工阶段的实践与应用方案(90页PPT)(文末有下载方式)
  • GoFrame框架深度解析:grpool的优势、最佳实践与踩坑经验
  • html:table表格
  • 十分钟用Docker搭建功能齐全的Poste.io邮件服务器
  • 全感官交互革命:当 AI 大模型学会 “看、听、说、创”
  • lib和dll介绍和VS2019生成实例
  • 大模型的第一天学习-LM studio的安装和本地大模型搭建
  • IntelliJ IDEA 使用教程
  • Python Cookbook-6.16 用 Borg 惯用法来避免“单例”模式
  • 系统思考与第一性原理
  • XCTF-pwn(二)
  • 从 Eclipse Papyrus / XText 转向.NET —— SCADE MBD技术的演化
  • MATLAB绘制局部放大图
  • 环境搭建:开启 Django 开发之旅
  • C++11新特性_标准库_正则表达式库
  • 如何理解 MCP 和 A2A 的区别?|AI系统架构科普
  • AI算法可视化:如何用Matplotlib与Seaborn解释模型?
  • 读懂 Vue3 路由:从入门到实战
  • 首都航空:太原至三亚航班巡航阶段出现机械故障,已备降南宁机场
  • 英国传统两党受挫地方选举后反思,改革党异军突起“突破想象”
  • 抢抓消费旺季:五一假期,多地党政主官调研外贸优品展销活动
  • 美股三大指数均收涨超1%,热门中概股强势拉升
  • 亚马逊一季度利润增超六成:云业务增速放缓,警告关税政策或影响业绩指引
  • 魔都眼|买买买,老铺黄金新店开业被挤爆:有人排队5小时