MyBatis实战指南(七)MyBatis缓存机制
MyBatis实战指南(七)MyBatis缓存机制
- 前言
- 一、 为什么需要缓存?
- 二、 MyBatis的缓存层次
- 三、 一级缓存
- 3.1 一级缓存(本地缓存)
- 3.2 一级缓存的工作原理
- 3.3 一级缓存示例
- 3.4 一级缓存的特点
- 3.5 一级缓存的优缺点
- 四、 二级缓存
- 4.1 什么是二级缓存?
- 4.2 为什么需要二级缓存?
- 4.3 如何配置二级缓存?
- 4.4 自定义二级缓存配置
- 4.5 可用的清除策略
- 4.6 二级缓存的特点
- 4.7 二级缓存的使用示例
- 五、 一级缓存和二级缓存的对比表格
前言
- 在上一篇博客中,我们深入解析了 MyBatis XML 实战的核心要点,围绕自动映射机制展开全面讲解,从字段命名匹配规则到驼峰转换配置(mapUnderscoreToCamelCase),再到自动映射行为控制(autoMappingBehavior),通过具体案例演示了如何利用 MyBatis 的自动映射能力简化对象与数据库的映射流程。
- 本篇博客将聚焦 MyBatis 的缓存优化体系,从底层原理到实战配置进行分层拆解
我的个人主页,欢迎来阅读我的其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343
我的MyBatis实战指南知识文章专栏
欢迎来阅读指出不足
https://blog.csdn.net/2402_83322742/category_12969707.html?spm=1001.2014.3001.5482
一、 为什么需要缓存?
- 想象一下,你有一个网站,每次用户访问页面都要从数据库查询相同的数据。
- 如果这个页面每天有1000人访问,那就意味着数据库要执行1000次相同的查询。
- 这会给数据库带来很大的压力,甚至可能导致数据库崩溃。
缓存就是解决这个问题的好办法。
它就像一个临时存储区,把经常访问的数据存起来,下次再需要这些数据时,直接从缓存中拿,而不需要再去访问数据库,这样可以大大提高系统性能。
二、 MyBatis的缓存层次
MyBatis有两级缓存机制:
- 一级缓存(本地缓存):默认开启,只在当前会话(SqlSession)中有效
- 二级缓存(全局缓存):需要手动配置,可以在多个会话之间共享
三、 一级缓存
3.1 一级缓存(本地缓存)
一级缓存是MyBatis的默认缓存机制,它是会话级别的缓存。这意味着:
- 同一个SqlSession中执行相同的查询,只会执行一次数据库查询,后续查询会直接从缓存中获取结果
- 当SqlSession关闭后,该会话的一级缓存也会被清空
- 当执行insert、update、delete操作时,会清空当前会话的一级缓存
3.2 一级缓存的工作原理
- 当你执行一个查询时,MyBatis首先会在当前会话的缓存中查找是否有相同的查询结果
- 如果有,直接从缓存中返回结果,不再执行数据库查询
- 如果没有,执行数据库查询,并将结果存入缓存
3.3 一级缓存示例
// 创建第一个会话
SqlSession session1 = sqlSessionFactory.openSession();
try {// 第一次查询,会执行SQLUser user1 = session1.selectOne("selectUserById", 1);System.out.println(user1.getName());// 第二次查询相同ID,不会执行SQL,直接从缓存获取User user2 = session1.selectOne("selectUserById", 1);System.out.println(user2.getName());// 两次查询获取的是同一个对象引用System.out.println(user1 == user2); // 输出 true
} finally {session1.close();
}// 创建第二个会话
SqlSession session2 = sqlSessionFactory.openSession();
try {// 在新会话中查询相同ID,会执行SQLUser user3 = session2.selectOne("selectUserById", 1);System.out.println(user3.getName());
} finally {session2.close();
}
3.4 一级缓存的特点
- 自动启用:不需要任何配置,默认就会使用
- 会话隔离:不同会话之间的缓存互不影响
- 轻量级:实现简单,开销小
- 生命周期短:随会话关闭而清空
3.5 一级缓存的优缺点
优点:
- 实现简单,不需要额外配置
- 减少了同一会话内的重复查询,提高了性能
- 对应用透明,不需要修改业务代码
缺点:
- 作用域太小,仅限于当前会话
- 不能在应用重启后保留
- 无法在分布式环境中共享
四、 二级缓存
4.1 什么是二级缓存?
二级缓存是MyBatis的全局缓存,它可以在多个SqlSession之间共享数据。这意味着:
- 不同的会话查询相同的数据时,可以从同一个缓存中获取
- 应用程序重启前,缓存数据会一直存在(除非被手动刷新)
- 适合缓存那些不经常变化、经常被查询的数据
4.2 为什么需要二级缓存?
一级缓存虽然能提高同一会话内的查询性能,但在以下场景下显得不足:
- 跨会话查询相同数据时,仍然会多次访问数据库
- 在分布式系统中,不同服务实例无法共享缓存
- 对于高并发系统,一级缓存的作用有限
二级缓存正是为了解决这些问题而设计的。
4.3 如何配置二级缓存?
要启用二级缓存,只需在SQL映射文件中添加<cache/>
标签:
<mapper namespace="com.example.mapper.UserMapper"><!-- 启用二级缓存 --><cache/><!-- 查询语句 --><select id="selectUserById" resultType="User">SELECT * FROM users WHERE id = #{id}</select>
</mapper>
这行简单的配置会产生以下效果:
- 该映射文件中所有SELECT语句的结果会被缓存
- 所有INSERT、UPDATE、DELETE语句会刷新缓存
- 默认使用LRU(最近最少使用)策略清除缓存
- 最多存储1024个对象引用
- 缓存对象可读写(返回的是对象副本)
4.4 自定义二级缓存配置
你可以通过<cache/>
标签的属性来自定义缓存行为:
<cacheeviction="FIFO" <!-- 清除策略:先进先出 -->flushInterval="60000" <!-- 刷新间隔:60秒 -->size="512" <!-- 缓存大小:最多512个对象 -->readOnly="true"/> <!-- 只读缓存:提高性能但返回共享对象 -->
4.5 可用的清除策略
MyBatis提供了四种缓存清除策略:
- LRU(默认):移除最长时间不被使用的对象
- FIFO:按对象进入缓存的顺序移除
- SOFT:基于JVM垃圾回收器状态和软引用规则移除
- WEAK:更激进的回收策略,基于弱引用规则移除
4.6 二级缓存的特点
- 全局共享:多个SqlSession可以共享同一个缓存
- 需要手动配置:默认不启用,必须在映射文件中声明
- 会话间隔离:每个命名空间(通常对应一个Mapper接口)有独立的缓存
- 事务支持:在事务提交或回滚时,缓存会被正确刷新
- 序列化要求:缓存对象必须实现
Serializable
接口
4.7 二级缓存的使用示例
// 第一个会话
SqlSession session1 = sqlSessionFactory.openSession();
try {UserMapper mapper1 = session1.getMapper(UserMapper.class);User user1 = mapper1.selectUserById(1); // 执行SQL查询session1.commit(); // 提交事务,将结果存入二级缓存
} finally {session1.close();
}// 第二个会话
SqlSession session2 = sqlSessionFactory.openSession();
try {UserMapper mapper2 = session2.getMapper(UserMapper.class);User user2 = mapper2.selectUserById(1); // 直接从二级缓存获取System.out.println(user2.getName());
} finally {session2.close();
}
五、 一级缓存和二级缓存的对比表格
对比维度 | 一级缓存(本地缓存) | 二级缓存(全局缓存) |
---|---|---|
作用域 | 仅在当前SqlSession 会话内有效 | 跨SqlSession 会话共享,属于全局范围 |
默认状态 | 自动启用(无需配置) | 需手动在Mapper文件中添加<cache/> 标签启用 |
缓存生命周期 | 随SqlSession 关闭而清空 | 随应用重启或手动刷新才会清空 |
数据共享 | 仅在当前会话内共享,不同会话间不共享 | 同一Mapper命名空间下的所有会话共享 |
刷新机制 | 执行insert/update/delete 操作时自动清空当前会话缓存 | 执行insert/update/delete 操作时清空全局缓存 |
缓存策略配置 | 无需配置,使用固定策略 | 可自定义配置(清除策略、刷新间隔、缓存大小等) |
缓存对象要求 | 无特殊要求(直接引用对象) | 对象需实现Serializable 接口(支持序列化) |
性能影响 | 开销小(轻量级,无序列化开销) | 存在序列化/反序列化开销,可通过readOnly 优化 |
适用场景 | 同一会话内的重复查询、短生命周期事务 | 跨会话的重复查询、不常更新且频繁访问的数据 |
分布式支持 | 不支持(仅单JVM内有效) | 原生不支持,需集成第三方缓存(如Redis、Memcached) |
典型场景示例 | 单事务内多次查询同一数据 | 系统字典表、配置表的全局缓存 |
以上就是这篇博客的全部内容,下一篇我们将继续探索MyBatis的更多精彩内容。
我的个人主页,欢迎来阅读我的其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343
我的MyBatis实战指南知识文章专栏
欢迎来阅读指出不足
https://blog.csdn.net/2402_83322742/category_12969707.html?spm=1001.2014.3001.5482
非常感谢您的阅读,喜欢的话记得三连哦 |