一文让你彻底搞懂Mybatis之缓存机制

编译软件:IntelliJ IDEA 2019.2.4 x64
操作系统:win10 x64 位 家庭版
Maven版本:apache-maven-3.6.3
Mybatis版本:3.5.6


文章目录

  • 一. 缓存是什么?
  • 二. 为什么要使用缓存?
  • 三. Mybatis中的缓存分哪几种?
    • 3.1 Mybatis缓存机制之一级缓存
    • 3.2 Mybatis缓存机制之二级缓存
    • 3.3 Mybatis中缓存机制之第三方缓存【以EhCache为例】


一. 缓存是什么?

一说到缓存,我们可能都会想到Cashe,这里摘自百度百科对它的解释:它原本是指访问速度比一般随机存取存储器(RAM)快的一种高速存储器,通常它不像系统主存那样使用DRAM技术,而使用昂贵但较快速的SRAM技术。缓存的设置是所有现代计算机系统发挥高性能的重要因素之一。它的工作原理是当CPU要读取一个数据时,首先从CPU缓存中查找,找到就立即读取并送给CPU处理;没有找到,就从速率相对较慢的内存中读取并送给CPU处理,同时把这个数据所在的数据块调入缓存中,可以使得以后对整块数据的读取都从缓存中进行,不必再调用内存。

在这里插入图片描述
以上部分内容可能看不懂,没关系,你只需要知道正是由于这样的读取机制,使得CPU读取缓存的命中率非常高(大多数CPU可达90%左右),也就是说CPU下一次要读取的数据90%都在CPU缓存中,只有大约10%需要从内存读取。这样大大节省了CPU直接读取内存的时间,非常快捷!!!

在这里插入图片描述

简而言之,以cpu中的缓存举例,缓存其实就是数据交换的缓冲区【又称Cashe】,是存贮数据(使用频繁的数据)的临时地方。当用户查询数据,首先在缓存中寻找,如果找到了则直接执行。如果找不到,则去数据库中查找


二. 为什么要使用缓存?

举个生活中的例子,当我们在线观看视频时,以哔哩哔哩网站为例,你会发现,底下的进度条会实时显示蓝色,白色等两种颜色。不难发现,蓝色代表的是视频实际播放的进度,而白色代表的是视频实时预先缓存的进度。如下所示。

在这里插入图片描述
这种让用户一边下载一边观看、收听,而不要等整个文件下载到自己的计算机上才可以观看的网络传输技术,就是鼎鼎大名的流媒体技术。该技术的原理是先在使用者端的计算机上创建一个缓冲区,在播放前预先下一段数据作为缓冲,在网路实际连线速度小于播放所耗的速度时,播放程序就会取用一小段缓冲区内的数据,这样可以避免播放的中断,也使得播放品质得以保证。该技术在很多音频影视网站上被大量使用,旨在丰富用户的使用体验并提高音频的播放性能。
在这里插入图片描述
而程序中的缓存【Mybatis缓存】,亦是如此,Mybatis使用缓存优势可以提高查询效率,并降低服务器的压力。它的本质就是用利用空间换时间,牺牲数据的实时性,以服务器内存中的数据暂时代替从数据库读取最新的数据,减少数据库IO,减轻服务器压力,减少网络延迟,加快页面打开速度。


三. Mybatis中的缓存分哪几种?

👉分类

  1. 一级缓存
  2. 二级缓存
  3. 第三方缓存

在这里插入图片描述

3.1 Mybatis缓存机制之一级缓存

👉概述

一级缓存【本地缓存(Local Cache)或SqlSessiona级别缓存】

🤔什么是SqlSessiona?

SqlSession是一个会话,相当于JDBC中的一个Connection对象,是整个Mybatis运行的核心,它是MyBatis的关键对象,是执行持久化操作的独享,类似于JDBC中的Connection。它是应用程序与持久层之间执行交互操作的一个单线程对象,也是MyBatis的核心接口之一

👉特点

  • 一级缓存默认开启
  • 不能关闭
  • 可以清空

💡 :有点类似于使用腾讯视频网站去看电影,电影观看进度条前的那一小段灰色的进度条

👉缓存原理

  1. 第一次获取数据时,先从数据库中加载数据,将数据缓存至Mybatis一级缓存中【缓存底层实现原理Map,key:hashCode+查询的Sqlld+编写的sal查询语句+参数】
  2. 以后再次获取数据时,先从一级缓存中获取,如未获取到数据,再从数据库中获取数据

不信?请看如下测试代码的体现

代码示例如下:

@Test
public void test04(){try {String resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);//通过SqlSessionFactory对象调用openSession();SqlSession sqlSession = sqlSessionFactory.openSession();//获取EmployeeMapper的代理对象EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);//第一次调用selectEmpByOneOpr(1);List<Employee> employees = employeeMapper.selectEmpByOneOpr(1);System.out.println(employees);System.out.println("---------------------------------------------");//第二次调用selectEmpByOneOpr(1);List<Employee> employees1 = employeeMapper.selectEmpByOneOpr(1);System.out.println(employees1);} catch (IOException e) {e.printStackTrace();}
}

在这里插入图片描述

👉一级缓存的五种失效情况

  1. 不同的SqlSession对应不同的一级缓存

  2. 同一个SqlSession但是查间条件不同

  3. 同一个SqlSession两次查询期间执行了任何一次增删改操作

    执行任何一次的增删改操作会默认清空一级缓存

  4. 同一个SqlSession两次查询期间手动清空了缓存

    如何手动清空一级缓存?

    sqlSession.clearCache()
    
  5. 同一个SqlSessioni两次查询期间提交了事务

    sqlSession.commit()
    

3.2 Mybatis缓存机制之二级缓存

👉概述

二级缓存【全局作用域缓存】是SqlSessionFactory级别的缓存

👉特点

  • 二级缓存默认关闭,需要开启才能使用

  • 二级缓存需要提交sqlSession或关闭sqlSessionl时,才会缓存。

👉二级缓存使用的步骤

①全局配置文件中开启二级缓存“<setting name="cacheEnab1ed"value=“true”/>”

②需要使用二级缓存的映射文件处使用cache配置缓存

③注意:POJO(Java Bean【java的实体类】)需要实现Serializable接口

④关闭sqlSession或提交sqlSessionl时,将数据缓存到二级缓存

👉用法案例

演示二级缓存的效果

代码示例如下:

①全局配置文件中开启二级缓存

在这里插入图片描述

②这里假设映射文件EmployeeMapper.xml使用cache配置缓存

在这里插入图片描述

③在映射文件EmployeeMapper.xml对应的pojo类实现Serializable接口

在这里插入图片描述

④测试

@Test
public void test04(){try {String resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);//通过SqlSessionFactory对象调用openSession()获取sqlSession对象;SqlSession sqlSession = sqlSessionFactory.openSession();//通过sqlSession对象调用getMapper(EmployeeMapper.class)以获取EmployeeMapper的代理对象EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);//第一次调用selectEmpByOneOpr(1);List<Employee> employees = employeeMapper.selectEmpByOneOpr(1);System.out.println(employees);//关闭sqlsession,目的是为了将数据加载到二级缓存中sqlSession.close();System.out.println("---------------------------------------------");//重新获取sqlsession对象SqlSession sqlSession1 =sqlSessionFactory.openSession();//使用sqlsession对象调用etMapper(EmployeeMapper.class)获取EmployeeMapper接口的代理对象mapperEmployeeMapper mapper = sqlSession1.getMapper(EmployeeMapper.class);//调用selectEmpByOneOpr(1);List<Employee> employees1 = mapper.selectEmpByOneOpr(1);System.out.println(employees1);} catch (IOException e) {e.printStackTrace();}
}

在这里插入图片描述

👉底层原理

  1. 第一次获取数据时,先从数据库中获取数据,将数据缓存至一级缓存;当提交或关闭SqlSessionl时,将数据缓存至二级缓存
  2. 以后再次获取数据时,先从一级缓存中获取数据,如果一级缓存没有指定数据,再去二级缓存中获取数据。如果二级缓存也没有指定数据时,需要去数据库中获取数据

👉二级缓存相关属性(在设置了cash的映射文件中设置以下属性)

  • eviction=“FIFO"缓存清除【回收】策略

    • LU-最近最少使用的移除最长时间不被使用的对象
    • FFO-先进先出按对象进入缓存的顺序来移除它们
  • flushlnterval刷新间隔,单位毫秒,默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新

  • size引用数目,正整数,代表缓存最多可以存储多少个对象,太大容易导致内存益出

  • readOnly只读,true/false

    • true只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势
    • false读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false

👉二级缓存的失效情况

在两次查询之间,执行增删改操作,会同时清空一级缓存和二级缓存

sqlSession.clearCache()只是用来清除一级缓存

🤔思考

执行两次查询操作(查询条件相同且查询语句相同),中间使用sqlsession.clearCache(),然后关闭第一个sqlsession对象,又新建一个sqlsession对象,执行第二次查询,是否会导致二级缓存失效?

我的推测: 会失效

测试代码如下:

@Test
public void test05() {try {String resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);//通过SqlSessionFactory对象调用openSession()获取sqlSession对象;SqlSession sqlSession = sqlSessionFactory.openSession();//通过sqlSession对象调用getMapper(EmployeeMapper.class)以获取EmployeeMapper的代理对象EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);//第一次调用selectEmpByOneOpr(1);List<Employee> employees = employeeMapper.selectEmpByOneOpr(1);System.out.println(employees);//清空一级缓存sqlSession.clearCache();//需要先关闭sqlsession对象,以缓存数据到二级缓存sqlSession.close();System.out.println("---------------------------------------------");//重新获取sqlsession对象SqlSession sqlSession1 = sqlSessionFactory.openSession();//使用sqlsession对象调用etMapper(EmployeeMapper.class)获取EmployeeMapper接口的代理对象mapperEmployeeMapper mapper = sqlSession1.getMapper(EmployeeMapper.class);/* //在两个查询之间执行一次更新操作,目的是为了清空二级缓存Employee e = new Employee();e.setId(1);e.setSalary(25000.0);mapper.updateEmp(e);*///调用selectEmpByOneOpr(1);List<Employee> employees1 = mapper.selectEmpByOneOpr(1);System.out.println(employees1);} catch (IOException e) {e.printStackTrace();}}

在这里插入图片描述

推测错了,二级缓存仍然可以命中该查询结果

⭐原因分析

当第一次执行某个 SQL 语句时,该 SQL 语句的缓存条目会被添加到二级缓存中;而在清空当前 SqlSession 对象的一级缓存时,并没有清空对应的 SQL 语句在二级缓存中的缓存条目,因此该 SQL 语句的缓存条目仍然存在于二级缓存中,即使这个二级缓存还没有和数据库同步。

当新建另一个 SqlSession 对象,执行相同的查询操作且查询条件和前一次查询操作相同时,MyBatis 将会先从一级缓存中尝试获取数据。由于已经执行了 sqlsession.clearCache() 清空了当前 SqlSession 的一级缓存,因此一级缓存会命中失败,但是 MyBatis 可以从二级缓存中获取到之前查询过的结果集,返回给我当前的查询操作结果。

3.3 Mybatis中缓存机制之第三方缓存【以EhCache为例】

👉概述

EhCache【第三方缓存】是一个纯ava的进程内缓存框架

👉使用步骤

①导入jar包

代码示例如下:

<!-- 导入ehcache的jar包 -->
<dependency><groupId>org.mybatis.caches</groupId><artifactId>mybatis-ehcache</artifactId><version>1.0.3</version>
</dependency><!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<!-- 导入一个日志的jar包 -->
<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.6.2</version>
</dependency>

②编写ehcache的配置文件(resources目录下)【ehcache.xml】

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:noNamespaceSchemaLocation="../config/ehcache.xsd"><!-- 磁盘保存路径 --><diskStore path="E:\mybatis\ehcache" /><defaultCachemaxElementsInMemory="512"maxElementsOnDisk="10000000"eternal="false"overflowToDisk="true"timeToIdleSeconds="120"timeToLiveSeconds="120"diskExpiryThreadIntervalSeconds="120"memoryStoreEvictionPolicy="LRU"></defaultCache>
</ehcache>

③加载第三方缓存【映射文件】

<!--  此处加载第三方缓存ehcache  -->
<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>

④开始使用

@Test
public void test04(){try {String resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);//通过SqlSessionFactory对象调用openSession()获取sqlSession对象;SqlSession sqlSession = sqlSessionFactory.openSession();//通过sqlSession对象调用getMapper(EmployeeMapper.class)以获取EmployeeMapper的代理对象EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);//第一次调用selectEmpByOneOpr(1);List<Employee> employees = employeeMapper.selectEmpByOneOpr(1);System.out.println(employees);//关闭sqlsession,目的是为了将数据加载到二级缓存中sqlSession.close();System.out.println("---------------------------------------------");//重新获取sqlsession对象SqlSession sqlSession1 =sqlSessionFactory.openSession();//使用sqlsession对象调用etMapper(EmployeeMapper.class)获取EmployeeMapper接口的代理对象mapperEmployeeMapper mapper = sqlSession1.getMapper(EmployeeMapper.class);//调用selectEmpByOneOpr(1);List<Employee> employees1 = mapper.selectEmpByOneOpr(1);System.out.println(employees1);} catch (IOException e) {e.printStackTrace();}
}

在这里插入图片描述

在这里插入图片描述

❗注意

①第三方缓存,需要建立在二级缓存基础上【需要开启二级缓存,第三方缓存才能生效】

②如何让第三方缓存失效?

将二级缓存设置失效即可【在两次查询之间,进行一次增删改操作以清除二级缓存】

代码示例如下:

@Test
public void test04(){try {String resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);//通过SqlSessionFactory对象调用openSession()获取sqlSession对象;SqlSession sqlSession = sqlSessionFactory.openSession();//通过sqlSession对象调用getMapper(EmployeeMapper.class)以获取EmployeeMapper的代理对象EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);//第一次调用selectEmpByOneOpr(1);List<Employee> employees = employeeMapper.selectEmpByOneOpr(1);System.out.println(employees);//关闭sqlsession,目的是为了将数据加载到二级缓存中sqlSession.close();System.out.println("---------------------------------------------");//重新获取sqlsession对象SqlSession sqlSession1 =sqlSessionFactory.openSession();//使用sqlsession对象调用etMapper(EmployeeMapper.class)获取EmployeeMapper接口的代理对象mapperEmployeeMapper mapper = sqlSession1.getMapper(EmployeeMapper.class);//在两个查询操作之间执行一次更新操作,目的是为了清空二级缓存Employee e=new Employee();e.setId(1);e.setSalary(25000.0);mapper.updateEmp(e);//调用selectEmpByOneOpr(1);List<Employee> employees1 = mapper.selectEmpByOneOpr(1);System.out.println(employees1);} catch (IOException e) {e.printStackTrace();}
}

在这里插入图片描述


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/11423.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySql基本操作

在了解具体的MySql操作之前&#xff0c;我们需要了解一些基本的sql语句注意事项&#xff0c;如下所示&#xff1a; 每一条sql语句都需要以英文 ; 作为结尾&#xff1b;sql语句当中的关键字不区分大小写&#xff0c;不区分双引号和单引号&#xff1b;sql中库名称、表名称和字段…

Windows 不同方式打开的cmd/dos窗口属性配置不同

文章目录 1. 默认值&#xff08;控制台窗口&#xff09;属性2. "C:\Windows\System32\cmd.exe" 属性3. "命令提示符"属性4. 自定义某标题cmd窗口属性5. cmd快捷方式的属性总结 最近在写某个批处理脚本时&#xff0c;意外发现 Windows系统中&#xff0c;在不…

HBuilder 编辑器终端窗口无法输入,未响应的解决方案

HBuilder 编辑器终端窗口无法输入&#xff0c;未响应的解决方案 一、找到 HBuilder 安装目录 找到 main.js HBuilderX - plugins - builtincef3terminal - script - main.js 二、编辑 main.js 将 main.js 文件中的 powershell.exe 和 cmd.exe 路径都改为绝对路径 C:/Windows…

传染病模型

title: 传染病模型 date: 2023-7-24 10:55:00 updated: 2023-7-24 10:55:00 tags: 算法数学建模传染病模型matlab categories: 数学建模 传染病模型中的符号表示 SI模型&#xff08;艾滋传染模型&#xff09; %% 直接求微分方程的解析解 dsolve(Dx1 -0.1 * x1 * x2 / 1000, D…

SSIS对SQL Server向Mysql数据转发表数据 (三)

1、在控制流界面&#xff0c;在左侧的组件里&#xff0c;添加一个“序列容器组件”和一个“数据流任务组件” 2、双击数据流任务&#xff0c;进入到数据流界面&#xff0c;然后再在左面添加一个OLE DB 源组件、目标源组件 3、右键源组件&#xff0c;编辑&#xff0c;选择好相关…

2023年 React 最佳学习路线

CSS CSS JavaScript JavaScript TypeScript 目前没有找到比其他文档好很多的文档地址 可以先看官网 React 新版 React 官方文档无敌 React React-router-dom V5 V6 Webpack webpack Antd antd

神码ai火车头伪原创设置【php源码】

大家好&#xff0c;给大家分享一下python考什么内容&#xff0c;很多人还不知道这一点。下面详细解释一下。现在让我们来看看&#xff01; 火车头采集ai伪原创插件截图&#xff1a; 1、Python 计算机二级都考什么 Python要到什么程度 考试内容 一、Python语言的基本语法元素…

Jenkins 拉取 GitHub 私有仓库失败问题

添加仓库的时候提示 stderr: fatal: Cannot prompt because user interactivity has been disabled. 把在 GitHub账户设置中生成的个人访问令牌填到地址里

技术分享 | App测试时常用的adb命令你都掌握了哪些呢?

adb 全称为 Android Debug Bridge&#xff08;Android 调试桥&#xff09;&#xff0c;是 Android SDK 中提供的用于管理 Android 模拟器或真机的工具。 adb 是一种功能强大的命令行工具&#xff0c;可让 PC 端与 Android 设备进行通信。adb 命令可执行各种设备操作&#xff0…

cnvd水证书

日常水个证书 本人比较菜&#xff0c;挖cnvd总是挖不倒漏洞&#xff0c;只能从弱口令入手了&#xff0c;一般使用fofa脚本批量跑。 过程&#xff1a; 日常在佛法中我们总是会遇见设备弱口或系统弱口令 一般是看登录成功的特征和口令&#xff0c;写一个对应的弱口令检测脚本…

Linux虚拟机(lvm)报Unmount and run xfs_repair

问题 linux系统没有正常关机&#xff0c;今天启动虚拟机无法进入系统&#xff0c;提示metadata corruption deleted at xxxx&#xff1b; Unmount and run xfs_repair 分析 主机异常掉电后里面的虚拟机无法启动&#xff0c;主要是损坏的分区 解决 看出来应该是dm-0分区损坏…

WebClient,HTTP Interface远程调用阿里云API

HTTP Interface Spring 允许我们通过定义接口的方式&#xff0c;给任意位置发送 http 请求&#xff0c;实现远程调用&#xff0c;可以用来简化 HTTP 远程访问。需要webflux场景才可 <dependency><groupId>org.springframework.boot</groupId><artifactId&…

Spring中最简单的过滤器和监听器

1. 过滤器概念引入 Filter也称之为过滤器&#xff0c;它是Servlet技术中最实用的技术&#xff0c;Web开发人员通过Filter技术&#xff0c;对web服务器管理的所有web资源&#xff1a;例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截&#xff0c;从而实现一些特殊的功…

uniapp 微信小程序:页面+组件的生命周期顺序

uniapp 微信小程序&#xff1a;页面组件的生命周期顺序 首页页面父组件子组件完整顺序参考资料 这个uniapp的微信小程序项目使用的是 VUE2 首页 首页只提供了一个跳转按钮。 <template><view><navigator url"/pages/myPage/myPage?namejerry" hov…

【React】精选5题

第1题&#xff1a;简述下 React 的生命周期&#xff1f;每个生命周期都做了什么&#xff1f; React 组件的生命周期可以分为三个阶段&#xff1a;挂载阶段、更新阶段和卸载阶段。每个生命周期方法都有特定的目的和功能。 挂载阶段&#xff1a; constructor&#xff1a;组件的构…

6个高清图片素材网站,免费下载,值得推荐~

关于图片素材网站&#xff0c;我一直都在推荐这几个&#xff0c;免费下载&#xff0c;可商用&#xff0c;建议收藏起来~ 菜鸟图库 https://www.sucai999.com/pic.html?vNTYwNDUx 网站主要是为新手设计师提供免费素材的&#xff0c;素材的质量都很高&#xff0c;类别也很多&a…

Git移除commit过的大文件

前言&#xff1a;在提交推送本地更改至仓库时&#xff0c;误将大文件给提交了&#xff0c;导致push时报错文件过大&#xff0c;因此需要将已经commit的大文件移除后再push 若已知要删除的文件或文件夹路径&#xff0c;则可以从第4步开始 1.对仓库进行gc操作 $ git gc 2.查询…

23款奔驰S400豪华型升级后排电动腿托系统,提升后排乘坐舒适性

奔驰S400L后排座椅是不带腿托和脚托的&#xff0c;也没有一键躺平功能&#xff0c;相对于奔驰S级高配车型上配置的右边老板位座椅&#xff0c;舒适性就差强了一些。

【基于Spark的电影推荐系统】环境准备

概览 本科毕设做过电影推荐系统&#xff0c;但当时的推荐算法只有一个&#xff0c;现在已经忘记大部分了&#xff0c;当时也没有记录&#xff0c;因此写这个博客重新来记录一下。此外&#xff0c;技术栈由于快秋招原因来不及做过多的部分&#xff0c;因此只实现简单的功能&…

从零开始 Spring Cloud 7:Gateway

从零开始 Spring Cloud 7&#xff1a;Gateway 图源&#xff1a;laiketui.com Spring Cloud Gateway 是 Spring Cloud 的一个全新项目&#xff0c;该项目是基于 Spring 5.0&#xff0c;Spring Boot 2.0 和 Project Reactor 等响应式编程和事件流技术开发的网关&#xff0c;它旨…