深入理解MyBatis中的一级缓存与二级缓存

发布时间 - 2026-01-11 02:01:17    点击率:

前言

先说缓存,合理使用缓存是优化中最常见的,将从数据库中查询出来的数据放入缓存中,下次使用时不必从数据库查询,而是直接从缓存中读取,避免频繁操作数据库,减轻数据库的压力,同时提高系统性能。

一级缓存

一级缓存是SqlSession级别的缓存。在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构用于存储缓存数据。不同的sqlSession之间的缓存数据区域是互相不影响的。也就是他只能作用在同一个sqlSession中,不同的sqlSession中的缓存是互相不能读取的。

一级缓存的工作原理:

用户发起查询请求,查找某条数据,sqlSession先去缓存中查找,是否有该数据,如果有,读取;

如果没有,从数据库中查询,并将查询到的数据放入一级缓存区域,供下次查找使用。

但sqlSession执行commit,即增删改操作时会清空缓存。这么做的目的是避免脏读。

如果commit不清空缓存,会有以下场景:A查询了某商品库存为10件,并将10件库存的数据存入缓存中,之后被客户买走了10件,数据被delete了,但是下次查询这件商品时,并不从数据库中查询,而是从缓存中查询,就会出现错误。

既然有了一级缓存,那么为什么要提供二级缓存呢?

二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。二级缓存的作用范围更大。

还有一个原因,实际开发中,MyBatis通常和Spring进行整合开发。Spring将事务放到Service中管理,对于每一个service中的sqlsession是不同的,这是通过mybatis-spring中的org.mybatis.spring.mapper.MapperScannerConfigurer创建sqlsession自动注入到service中的。

每次查询之后都要进行关闭sqlSession,关闭之后数据被清空。所以spring整合之后,如果没有事务,一级缓存是没有意义的。

二级缓存

二级缓存原理:

二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。

UserMapper有一个二级缓存区域(按namespace分),其它mapper也有自己的二级缓存区域(按namespace分)。每一个namespace的mapper都有一个二级缓存区域,两个mapper的namespace如果相同,这两个mapper执行sql查询到数据将存在相同的二级缓存区域中。

开启二级缓存:

1,打开总开关

在MyBatis的配置文件中加入:

<span style="font-size:18px;"><settings> 
 <!--开启二级缓存--> 
 <setting name="cacheEnabled" value="true"/> 
</settings> </span> 

2,在需要开启二级缓存的mapper.xml中加入caceh标签

<span style="font-size:18px;"><cache/></span>

3,让使用二级缓存的POJO类实现Serializable接口

<span style="font-size:18px;">public class User implements Serializable {}</span> 

测试一下

<span style="font-size:18px;">@Test 
public void testCache2() throws Exception { 
 SqlSession sqlSession1 = sqlSessionFactory.openSession(); 
 SqlSession sqlSession2 = sqlSessionFactory.openSession(); 
 UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class); 
 User user1 = userMapper1.findUserById(1); 
 System.out.println(user1); 
 sqlSession1.close(); 
 UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class); 
 User user2 = userMapper2.findUserById(1); 
 System.out.println(user2); 
 sqlSession2.close(); 
}</span> 

输出结果:

<span style="font-size:18px;">DEBUG [main] - Cache Hit Ratio [com.iot.mybatis.mapper.UserMapper]: 0.0 
DEBUG [main] - Opening JDBC Connection 
DEBUG [main] - Created connection 103887628. 
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@631330c] 
DEBUG [main] - ==> Preparing: SELECT * FROM user WHERE id=? 
DEBUG [main] - ==> Parameters: 1(Integer) 
DEBUG [main] - <==  Total: 1 
User [id=1, username=张三, sex=1, birthday=null, address=null] 
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@631330c] 
DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@631330c] 
DEBUG [main] - Returned connection 103887628 to pool. 
DEBUG [main] - Cache Hit Ratio [com.iot.mybatis.mapper.UserMapper]: 0.5 
User [id=1, username=张三, sex=1, birthday=null, address=null]</span> 

我们可以从打印的信息看出,两个sqlSession,去查询同一条数据,只发起一次select查询语句,第二次直接从Cache中读取。

前面我们说到,Spring和MyBatis整合时, 每次查询之后都要进行关闭sqlSession,关闭之后数据被清空。所以spring整合之后,如果没有事务,一级缓存是没有意义的。那么如果开启二级缓存,关闭sqlsession后,会把该sqlsession一级缓存中的数据添加到namespace的二级缓存中。这样,缓存在sqlsession关闭之后依然存在。

总结:

对于查询多commit少且用户对查询结果实时性要求不高,此时采用mybatis二级缓存技术降低数据库访问量,提高访问速度。

但不能滥用二级缓存,二级缓存也有很多弊端,从MyBatis默认二级缓存是关闭的就可以看出来。
二级缓存是建立在同一个namespace下的,如果对表的操作查询可能有多个namespace,那么得到的数据就是错误的。

举个简单的例子:

订单和订单详情,orderMapper、orderDetailMapper。在查询订单详情时我们需要把订单信息也查询出来,那么这个订单详情的信息被二级缓存在orderDetailMapper的namespace中,这个时候有人要修改订单的基本信息,那就是在orderMapper的namespace下修改,他是不会影响到orderDetailMapper的缓存的,那么你再次查找订单详情时,拿到的是缓存的数据,这个数据其实已经是过时的。

根据以上,想要使用二级缓存时需要想好两个问题:

     1)对该表的操作与查询都在同一个namespace下,其他的namespace如果有操作,就会发生数据的脏读。

     2)对关联表的查询,关联的所有表的操作都必须在同一个namespace。

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对的支持。


# mybatis的缓存处理  # mybatis二级缓存配置  # mybatis  # 二级缓存  # 深入理解Mybatis二级缓存  # MyBatis关于二级缓存问题  # 详解Mybatis的二级缓存配置  # mybatis二级缓存的实现代码  # Mybatis如何通过注解开启使用二级缓存  # MyBatis中一级缓存和二级缓存的区别  # 在分布式环境下正确使用MyBatis二级缓存的最佳实践  # 多个  # 清空  # 如果没有  # 数据库中  # 就会  # 也有  # 都要  # 并将  # 下次  # 在同一个  # 有一个  # 自己的  # 时需  # 的是  # 这是  # 是在  # 都有  # 会有  # 好了  # 是他 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 如何在万网ECS上快速搭建专属网站?  韩国服务器如何优化跨境访问实现高效连接?  Laravel如何使用Gate和Policy进行权限控制_Laravel权限判定与策略规则配置  公司网站制作需要多少钱,找人做公司网站需要多少钱?  如何做网站制作流程,*游戏网站怎么搭建?  常州企业网站制作公司,全国继续教育网怎么登录?  PHP正则匹配日期和时间(时间戳转换)的实例代码  如何用搬瓦工VPS快速搭建个人网站?  Linux系统运维自动化项目教程_Ansible批量管理实战  北京网站制作费用多少,建立一个公司网站的费用.有哪些部分,分别要多少钱?  Laravel如何监控和管理失败的队列任务_Laravel失败任务处理与监控  如何在阿里云虚拟服务器快速搭建网站?  Android自定义listview布局实现上拉加载下拉刷新功能  Laravel模型关联查询教程_Laravel Eloquent一对多关联写法  详解jQuery中的事件  LinuxCD持续部署教程_自动发布与回滚机制  Laravel怎么使用Collection集合方法_Laravel数组操作高级函数pluck与map【手册】  ,网页ppt怎么弄成自己的ppt?  java中使用zxing批量生成二维码立牌  Laravel如何从数据库删除数据_Laravel destroy和delete方法区别  Laravel如何为API生成Swagger或OpenAPI文档  Laravel如何使用Gate和Policy进行授权?(权限控制)  香港服务器WordPress建站指南:SEO优化与高效部署策略  Laravel怎么在Blade中安全地输出原始HTML内容  湖南网站制作公司,湖南上善若水科技有限公司做什么的?  如何快速使用云服务器搭建个人网站?  node.js报错:Cannot find module &#39;ejs&#39;的解决办法  Laravel怎么设置路由分组Prefix_Laravel多级路由嵌套与命名空间隔离【步骤】  如何在阿里云虚拟主机上快速搭建个人网站?  制作ppt免费网站有哪些,有哪些比较好的ppt模板下载网站?  Laravel Blade组件怎么用_Laravel可复用视图组件的创建与使用  如何快速搭建高效WAP手机网站?  Laravel的HTTP客户端怎么用_Laravel HTTP Client发起API请求教程  Laravel如何与Inertia.js和Vue/React构建现代单页应用  Python制作简易注册登录系统  高性价比服务器租赁——企业级配置与24小时运维服务  用yum安装MySQLdb模块的步骤方法  桂林网站制作公司有哪些,桂林马拉松怎么报名?  简历在线制作网站免费版,如何创建个人简历?  Laravel怎么使用artisan命令缓存配置和视图  Laravel Docker环境搭建教程_Laravel Sail使用指南  Laravel队列由Redis驱动怎么配置_Laravel Redis队列使用教程  laravel怎么在请求结束后执行任务(Terminable Middleware)_laravel Terminable Middleware请求结束任务执行方法  智能起名网站制作软件有哪些,制作logo的软件?  Laravel如何使用Vite进行前端资源打包?(配置示例)  高性能网站服务器配置指南:安全稳定与高效建站核心方案  如何在橙子建站上传落地页?操作指南详解  Laravel怎么上传文件_Laravel图片上传及存储配置  Python面向对象测试方法_mock解析【教程】  Windows10电脑怎么设置虚拟光驱_Win10右键装载ISO镜像文件