Mybatis缓存机制详解(一级缓存+二级缓存)

什么是一级缓存

MyBatis 包含了一个非常强大的 查询缓存特性,它可以非常方便地配置和定制。MyBatis 3 中的缓存实现的很多改进都已经实现了,使得它更加强大而且易于配置。mybatis 默认情况下只会开启一级缓存,也就是局部的 session 会话缓存

首先我们要知道什么是查询缓存?查询缓存又有什么作用?

功能:mybatis提供查询缓存,用于减轻数据压力,提高数据库性能。

如下图,每一个 session 会话都会有各自的缓存,这缓存是局部的,也就是所谓的一级缓存


什么情况下会命中一级缓存

  1. 相同的 sql参数
  2. 必须是在一个会话 Session 当中
  3. 必须是执行 相同的方法
  4. 必须是相同的 namespace (同一个命名空间 -> 同一个mapper文件)
  5. 不能够在查询之前执行 clearCache
  6. 中间不能执行 任何 update ,delete ,insert (会将SqlSession中的数据全部清空)

Mybatis的缓存机制详解

一级缓存是SqlSession级别的缓存。我们都知道在操作数据库时需要构造 sqlSession对象,而在sqlSession对象中有一个数据结构(HashMap)用于存储缓存数据。

如下图:

Mybatis的缓存机制详解

从图上,我们可以看出,一级缓存区域是根据SqlSession为单位划分的。每次查询都会先从缓存区域找,如果找不到就会从数据库查询数据,然后将查询到的数据写入一级缓存中。Mybatis内部存储缓存使用的是一个HashMap对象,key为 hashCode + sqlId + sql 语句。而value值就是从查询出来映射生成的java对象。而为了保证缓存里面的数据肯定是准确数据避免脏读,每次我们进行数据修改后(增、删、改操作)就会执行commit操作,清空缓存区域。


在这里插入图片描述
在这里插入图片描述

SqlSession和Executor都是接口 ,而实现是 DefaultSqlSession 和 CacheExecutor

  • Client相当于测试方法 Test1
  • User@Proxy 动态代理 (相当于一个伪皇帝,只是负责做转发的) == 代码中的UserMapper(代理对象) 实则没什么卵用
  • Executor 接口 才是去数据库中拿数据的跑腿的
  • SqlSession 是接口
  • 一级缓存的实现是通过 CacheExecutor 实现的

底层实现 详解!!

在这里插入图片描述
在这里插入图片描述

在selectById方法打入断点 以及底层 实现类 PrepetualCache中的getObject(Object key)打入断点
观察 debug 的堆栈信息 可以看出

  1. 上图 标记绿色的框内都是 动态代理的实现 没用任何意义 只是做了传递 所以忽略这一部分
  2. 再往上走 我们查询的是selectOne 但是还是会去查询SelectList再去转换 通过DefaultSqlSession实现

真正与缓存相关的是这几行的实现

真正与缓存相关的是这几行的实现

流程如下
在这里插入图片描述
在这里插入图片描述

这一步骤:

  • 可以看出缓存key中是包含了 方法和namespace和会话 这些必须相同才会去做一个缓存命中
  • 这里面封装了缓存唯一的key

在这里插入图片描述

  • DefaultSqlSession中有一个CacheExecutor
  • CacheExecutor 中有一个 Simpleexexutor
  • Simpleexexutor 中有一个叫 LocalCache (PerpetualCache类型)
  • LocalCache才是真正的存储缓存的地方
  • LocalCache 中有一个叫cache (Hashmap <Object,Object>类型的)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

一级缓存的获取 我们就打通了!!!

在这里插入图片描述

一级缓存的PUT流程

前几条流程与上图相同
在这里插入图片描述
Cache的生命周期和SqlSession相同

是否会存在数据不一致问题?

不会 因为数据库本身是具有事务隔离级别的 默认是可重复读 也就是说任何一个事务在它内部读到的数据都是一致的,所以并不存在数据不一致的问题


Mybatis二级缓存

在这里插入图片描述

二级缓存使用条件 必须配置namespace注解
put 二级缓存的时候 此会话必须关闭才能put (因为跨事务读 否则会出现脏读问题)

什么情况下会命中一级缓存

  1. 相同的 sql参数
  2. 必须是在一个会话 Session 当中
  3. 必须是执行 相同的方法
  4. 必须是相同的 namespace (同一个命名空间 -> 同一个mapper文件)
  5. 不能够在查询之前执行 clearCache
  6. 中间不能执行 任何 update ,delete ,insert (会将SqlSession中的数据全部清空)
  7. 二级缓存使用条件 必须配置Cachenamespace注解
  8. put 二级缓存的时候 此会话必须关闭才能put (因为跨事务读 否则会出现脏读问题)

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

示意图:

Mybatis的缓存机制详解

二级缓存区域是根据mapper的namespace划分的,**相同namespace的mapper查询数据放在同一个区域,**如果使用mapper代理方法每个mapper的namespace都不同,此时可以理解为二级缓存区域是根据mapper划分,也就是根据命名空间来划分的,如果两个mapper文件的命名空间一样,那样,不同的SqlSession之间就可以共享一个mapper缓存。

示意图:

Mybatis的缓存机制详解

在默认情况下是没有开启二级缓存的,除了局部的 session 缓存。而在一级缓存中我们也介绍了,不同的SqlSession之间的一级缓存是不共享的,所以如果我们用两个SqlSession去查询同一个数据,都会往数据库发送sql。

开启二级缓存后,SqlSession之间的数据就可以通过二级缓存共享了,和一级缓存一样,当执行了insert、update、delete等操作并commit提交后就会清空二级缓存区域。当一级缓存和二级缓存同时存在时,会先访问二级缓存,再去访问各自的一级缓存,如果都没有需要的数据,才会往数据库发送sql进行查询。

猜你喜欢

转载自blog.csdn.net/weixin_44284160/article/details/109271495