首页 > 其他分享 >MyBatis之一级缓存、二级缓存

MyBatis之一级缓存、二级缓存

时间:2023-06-02 17:24:37浏览次数:61  
标签:缓存 一级 SqlSession mybatis 二级缓存 线程 MyBatis

1、一级缓存

跟踪BaseExecutor可以看到,在query方法中,实现了缓存逻辑,当缓存不存在的时候,则调用实现类中的doQuery。

创建一级缓存的KEY:

查看缓存中是否存在,存在则直接返回,不存在则查询数据库:

查询数据库:

这就是Mybatis中的一级缓存,逻辑十分简单,可以从源码中看到,一级缓存是默认开启的并且在同一个线程中有效,同时查询完毕之后,立即建立缓存。

1)、一级缓存命中场景

mybatis中的一级缓存是存放在hashMap中的,之所以没有用线程安全的集合,主要是因为SqlSession都是线程不安全的,所以没有必须要。

1、运行时命中相关

① 同一个会话中(所以也被成为会话级缓存)

② Sql语句、参数相同

③ 相同的statementId(相同Sql和参数,在不同的statementId中也不行)

④ RowBounds相同(mybatis分页使用,可以理解为参数相同)

2、操作和配置相关

① 未手动清空缓存(session1.clearCache();)

② 未配置 @Options(flushCache = Options.FlushCachePolicy.TRUE),其实原理同上(在实验中发现必须要跟@Select标签一起使用才能生效)

③ 未执行 Update 语句(在源码中可以看到,只要更新操作就会调用 clearCache()方法)

④ 作用域为STATEMENT(在setting中配置 ,主要是减小了缓存的作用域,在子查询中还是会使用缓存的)

⑤ 使用事务提交、回滚操作

总结:

1、会话相关

2、参数条件相关

3、提交、事务、更新会清空

2)、一级缓存失效场景

在spring和mybaits中,会出现mybatis一级缓存失效的情况,主要原因是spring的封装,导致每次查询都会创建一个SqlSession,因为一级缓存是会话级缓存,所以导致失效。通过分析源码可知,在同一个事务情况下,spring 不会重新创建SqlSession,所以开启事务即可解决。

spring调用mybatis流程如下:

这里可以看到,在SqlSessionTemplate中是使用sqlSession代理进行调用的

继续追踪下去可以看出来,sqlSessionProxy代理是使用 动态代理 SqlSessionInterceptor 创建的,

下面查看 SqlSessionInterceptor 中获取SqlSession方法:

到这里获取到的SqlSession,就是执行sql的SqlSession

可以看到他其实从ThreadLocal(也就是一个事务中保存的SqlSession)变量中先获取了一下,是否存在 ,存在直接使用,不存在就重新创建。这也就是为什么开启事务就能命中一级缓存的主要原因。

可以看到sqlSession被spring中的 SqlSessionTemplate替换了,但是 SqlSessionTemplate 本身没有执行Sql的能力,在其中又采用动态代理获取了DefaultSqlSession。

2、二级缓存

mybatis中,实现二级缓存是使用CachingExecutor这个类实现的,同时采用装饰者模式,对查询进行调用,源码如下:

在CachingExecutor中,存在一个属性就是 Executor,而CachingExecutor本身只实现缓存相关的内容:

上面的源码可以看到,当缓存开启的时候,先查询缓存中是否存在,不存在则使用执行器(Executor)进行查询。

下面查看二级缓存怎么启动:

1、在setting中配置缓存开启:

2、在需要开启缓存的Mapper.xml文件中添加如下一行:

编写例子 :

虽然使用的 CachingExecutor 但是发现日志输出的缓存命中率是0,这是因为二级缓存需要提交之后才能建立缓存,打开cachingExecutor.commit(ture)

在平时开发的过程中,其实直接使用SqlSession进行sql的执行的,并不需要上面那么复杂的代码。

其他配置内容:

PS:

① flushCache 清空之后,需要提交才能生效,并且只要提交了,所有的缓存都会被清空。

和 @CacheNamespace 不是一个缓存空间,也就是使用@Select 期望命中缓存,则需要使用 @CacheNamespace,而xml中期望命中缓存,则需要使用,这里是需要注意的。

跟踪代码可知,

当开启缓存的时候,可以看到是先执行二级缓存,再执行一级缓存的。

1)、二级缓存存在的意义

1、二级缓存是跨线程的

2、一级缓存的作用范围相对小,二级缓存的生命周期为整个应用

2)、二级缓存的需求

1、存储的多元性(Redis、Mysql、内存等)

2、存储容量需要限定,就存在淘汰规则(FIFO[先进先出]、LRU[最近最少使用])

3、数据的过期清理

4、保证线程安全

5、缓存的命中率统计

6、序列化

3)、二级缓存组件的结构

mybatis二级缓存逻辑实现采用:装饰器+责任链的方式进行实现的。

其实也就是每个上述的需求点,都会对应一个集成Cache接口的类,并且这些类串联起来,最终实现缓存的逻辑。

debug可以看到如下:

4)、二级缓存命中条件

① 必须提交之后(就算sqlsession是自动提交的,也需要手动提交才能生效)

② Sql语句、参数相同

③ statementId 相同

④ 分页条件相同

为什么提交之后才能命中缓存?

主要是因为,二级缓存是跨线程访问的,为了防止其他线程的脏读。

每个会话都有自己的事务缓存管理,当会话读取的数据的时候,会暂时存放到事务管理器的暂存区中,只有commit之后,才提交到缓存区中,这个时候才能被其他的会话共享。不论是查询还是更新都是暂时放在暂存区中。

其中暂存区的多少取决于使用了多少个命名空间。

执行流程:

下面的源码可以看到,采用一个标志位clearOnCommit,进行维护,主要是因为,如果另一个会话对缓存的数据进行了update,但是没有commit的时候,update的数据值存在暂存区中。

以上是对mybatis的执行器和缓存机制的简单描述。

这里推荐mybatis讲解非常棒的福利:B站 传送门 真正的干活满满!~

同时盗用一下UP主的图片:

标签:缓存,一级,SqlSession,mybatis,二级缓存,线程,MyBatis
From: https://www.cnblogs.com/jundong2177/p/17452415.html

相关文章

  • [MyBatis]DAO层只写接口,不用写实现类
    团队开发一个项目,由老大架了一个框架,遇到了DAO层不用写接口了,我也是用了2次才记住这个事的,因为自己一直都是习惯于写DAO层的实现类,所以,习惯性的还是写了个实现类。于是遇到错误了。找不到那个方法。问了团队的人才知道,方法名和Mapper中配置的id名必须一样。实现:一、配置Spring集......
  • MyBatis常见好用的插件
    阅读文本大概需要3分钟。0x01:MybatisPageHelper分页插件在没有分页插件之前,写一个分页需要两条SQL语句,一条查询一条统计,然后才能计算出页码,这样的代码冗余而又枯燥,更重要的一点是数据库迁移,众所周知不同的数据库分页写法是不同的,而Mybatis不同于Hibernate的是它只提供动态SQL和结......
  • 执行计划缓存,Prepared Statement性能跃升的秘密
    摘要:一起看一下GaussDB(forMySQL)是如何对执行计划进行缓存并加速PreparedStatement性能的。本文分享自华为云社区《执行计划缓存,PreparedStatement性能跃升的秘密》,作者:GaussDB数据库。引言在数据库系统中,SQL(StructuredQueryLanguage)语句输入到系统后,一般要经历:词法语法解......
  • 在web应用中使用mybatis
    1. 实现功能:52银⾏账户转账1.1 使⽤技术:HTML + Servlet + MyBatis1.2 WEB应⽤的名称:bank2. 数据库表的设计和准备数据  523. 注意MyBatis对象作⽤域以及事务问题   573.1 MyBatis核⼼对象的作⽤域   573.1.1 SqlSessionFactoryBuilder这个类可以被实例化、使⽤......
  • ABP - 缓存模块(1)
    1.与.NETCore缓存的关系和差异ABP框架中的缓存系统核心包是Volo.Abp.Caching,而对于分布式缓存的支持,abp官方提供了基于Redis的方案,需要安装Volo.Abp.Caching.StackExchangeRedis集成包。默认的情况下,在我们使用ABPCLI创建ABP框架模板项目的时候已经集成了这个包......
  • 3月5日周老师缓存面试题资料
    找班主任,要周老师的路线发一下面试突击班,第一天开班有6000+人在线~!!!希望大家紧张起来,今年竞争尤其激烈,新来的同学要加把劲~!今天主题:缓存面试题周老师提升面试成功率给到对方你有思想,有个人见解redis为什么快,TPS/QPS是多少,解决了项目什么问题?1,我讲的redis第一版,之后今年新讲的redi......
  • mybatis 复杂类型返回
    功能:查询一个数据列表且每个数据中包含各自的子数据集合使用场景:1.当需要查询多订单数据且同时订单数据中需要包含订单明细数据时         2.当需要查询多评论数据且同时评论数据中需要包含评论回复数据时功能效果概述图:1.Dao 层定义packagecom.ljw.dao;......
  • Mybatis20_Mybatis的增删改查操作3
    一、MyBatis的插入数据操作1.编写UserMapper映射文件<?xmlversion="1.0"encoding="UTF-8"?><!DOCTYPEmapperPUBLIC"-//mybatis.org//DTDMapper3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mappername......
  • Mybatis20_MyBatis的相应API4
    一、SqlSession工厂构建器SqlSessionFactoryBuilder常用API:SqlSessionFactorybuild(InputStreaminputStream)通过加载mybatis的核心文件的输入流的形式构建一个SqlSessionFactory对象 其中,Resources工具类,这个类在org.apache.ibatis.io包中(ibatis是mybatis的前身......
  • 手写mybatis框架1
    引⼊相关依赖   35pom.xml<?xmlversion="1.0"encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="h......