Mybatis一级缓存与二级缓存

本文参考资源:

MyBatis 二级缓存全详解 - cxuan - 博客园

缓存

存在于内存中的临时数据

减少和数据库的交互次数,提高执行效率

适用于缓存的数据:

  • 经常查询并且不经常改变。
  • 数据的正确与否对最终结果影响不大

一级缓存

概述

Mybatis中的一级缓存指的是SqlSession级别的缓存。

一个SqlSession对象通常表示用户和Mybatis建立的一次会话,在这个会话中,重复执行完全相同的查询语句且不受影响的情况下就会使用一级缓存。

每当一个SqlSession对象创建时,它会持有一个Executor执行器,直接和Connection打交道,当我们第一次执行查询之后,查询的结果会同时存入到这个Executor执行器中的一块区域:

MyBatis 将缓存和对缓存相关的操作封装成了 Cache 接口中,Executor 接口的实现类 BaseExecutor 中拥有一个 Cache 接口的实现类 PerpetualCache,则对于 BaseExecutor 对象而言,它将使用 PerpetualCache 对象维护缓存。

Mybatis一级缓存与二级缓存

PerpetualCache 实现原理其实很简单,其内部就是通过一个简单的 HashMap<k,v> 来实现
的,没有其他的任何限制。

生命周期

  1. MyBatis 在开启一个数据库会话时,会创建一个新的 SqlSession 对象,SqlSession 对象中会有一个新的 Executor 对象,Executor 对象中持有一个新的 PerpetualCache 对象;当会话结束时,SqlSession 对象及其内部的 Executor 对象还有 PerpetualCache 对象也一并释放掉。
  2. 如果 SqlSession 调用了 close()方法,会释放掉一级缓存 PerpetualCache 对象,一级缓存将不可用;
  3. 如果 SqlSession 调用了 clearCache(),会清空 PerpetualCache 对象中的数据,但是该对象仍可使用;
  4. SqlSession 中执行了任何一个 update 操作(update()、delete()、insert()) ,都会清空PerpetualCache 对象的数据,但是该对象可以继续使用;

工作流程

  1. 对于某个查询,根据 statementId,params,rowBounds 来构建一个 key 值,根据这个 key 值去缓存 Cache 中取出对应的 key 值存储的缓存结果;
  2. 判断从 Cache 中根据特定的 key 值取的数据数据是否为空,即是否命中;
  3. 如果命中,则直接将缓存结果返回;
  4. 如果没命中:
    1. 去数据库中查询数据,得到查询结果;
    2. 将 key 和查询到的结果分别作为 key,value 对存储到 Cache 中;
    3. 将查询结果返回;

一级缓存是一个粗粒度的缓存,没有更新缓存和缓存过期的概念

二级缓存

概述

MyBatis一级缓存最大的共享范围就是一个SqlSession内部,那么如果多个 SqlSession 需要共享缓存,则需要开启二级缓存,开启二级缓存后,会使用 CachingExecutor 装饰 Executor,进入一级缓存的查询流程前,先在CachingExecutor 进行二级缓存的查询,具体的工作流程如下所示

Mybatis一级缓存与二级缓存

当二级缓存开启后,同一个命名空间(namespace) 所有的操作语句,都影响着一个共同的 cache,也就是二级缓存被多个 SqlSession 共享,是一个全局的变量。当开启缓存后,数据的查询执行的流程就是 二级缓存 -> 一级缓存 -> 数据库。

但二级缓存缓存的是数据而不是对象,当我们在使用二级缓存时,所缓存的类一定要实现 java.io.Serializable 接口,这种就可以使用序列化方式来保存对象。

使用

二级缓存默认是不开启的,需要以下操作开启:

  • 在Mybatis.xml总配置
<settings>
    <setting name="cacheEnabled" value="true"/>
</settings>

因为 cacheEnabled 的取值默认就为 true,所以这一步可以省略不配置。为 true 代表开启二级缓存;为false 代表不开启二级缓存

  • 在Mapper配置文件中配置
<cache/>

cache标签可以设置以下属性:

  • eviction: 缓存回收策略,有这几种回收策略
    • LRU - 最近最少回收,移除最长时间不被使用的对象
    • FIFO - 先进先出,按照缓存进入的顺序来移除它们
    • SOFT - 软引用,移除基于垃圾回收器状态和软引用规则的对象
    • WEAK - 弱引用,更积极的移除基于垃圾收集器和弱引用规则的对象

默认是 LRU 最近最少回收策略

  • flushinterval: 缓存刷新间隔,缓存多长时间刷新一次,默认不清空,设置一个毫秒值
  • readOnly: 是否只读;true 只读,MyBatis 认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。MyBatis 为了加快获取数据,直接就会将数据在缓存中的引用交给用户。不安全,速度快。读写(默认):MyBatis 觉得数据可能会被修改。
  • size : 缓存存放多少个元素
  • type: 指定自定义缓存的全类名(实现Cache接口即可)
  • blocking: 若缓存中找不到对应的key,是否会一直blocking,直到有对应的数据进入缓存。

  • 在Select标签中配置

<select id="" useCache="true">
    ...
</select>

注意

  • 二级缓存是以namespace为单位的,不同namespace下的操作互不影响。
  • insert,update,delete操作会清空所在namespace下的全部缓存。
  • 通常使用MyBatis Generator生成的代码中,都是各个表独立的,每个表都有自己的namespace。
  • 多表操作一定不要使用二级缓存,因为多表操作进行更新操作,一定会产生脏数据。

原创文章,作者:彭晨涛,如若转载,请注明出处:https://www.codetool.top/article/mybatis%e4%b8%80%e7%ba%a7%e7%bc%93%e5%ad%98%e4%b8%8e%e4%ba%8c%e7%ba%a7%e7%bc%93%e5%ad%98/