MyBatis缓存机制的设计与实现

Posted Java开发吧

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MyBatis缓存机制的设计与实现相关的知识,希望对你有一定的参考价值。

   本文主要讲解MyBatis非常棒的缓存机制的设计原理,给读者们介绍一下MyBatis的缓存机制的轮廓,然后会分别针对缓存机制中的方方面面展开讨论。

MyBatis将数据缓存设计成两级结构,分为一级缓存、二级缓存:


       一级缓存是Session会话级别的缓存,位于表示一次数据库会话的SqlSession对象之中,又被称之为本地缓存。一级缓存是MyBatis内部实现的一个特性,用户不能配置,默认情况下自动支持的缓存,用户没有定制它的权利(不过这也不是绝对的,可以通过开发插件对它进行修改)。


       基于PerpetualCache的HashMap的本地缓存,一级缓存的作用域为sqlSession,当sqlSession被flush或close之后,当前sqlSession中的所有缓存都将被清空。


       二级缓存是Application应用级别的缓存,它的是生命周期很长,跟Application的声明周期一样,也就是说它的作用范围是整个Application应用。


      和一级缓存的机制相同,默认也是采用PerpetualCache的HashMap存储,但二级缓存的作用域是mapper(namespace)。


一级缓存

在举例子之前,我们先来看看一级缓存的工作原理:

 我们在程序中第一次发起对用户id为1的用户信息进行查询时,会先去缓存中搜索是否有id为1的用户信息;如果没有,才会去数据库查询该用户的信息;得到用户信息后,将用户信息存储到一级缓存中;


第二次发起对用户id为1的查询,先去缓存中找是否存id为1的用户信息,缓存中有就会直接在缓存中取出相关数据;


如果sqlsession中执行了commit操作(增,删,改),sqlsession中的一级缓存会被清除,这样可以保证缓存中的信息时最新数据,避免脏读;


一级缓存测试

mybatis默认支持一级缓存,不需要在配置文件中设置;下面我们来看一级缓存的测试代码:


MyBatis缓存机制的设计与实现

 从执行结果中可以看出:


MyBatis缓存机制的设计与实现

       第一次查询发出了一条sql语句,但是第二次查询并没有发出,因为第二次查询直接从一级缓存中取出数据;


       但是,当我们将mybatis和Spring进行整合之后,事务将被控制在Service中:


MyBatis缓存机制的设计与实现

 一个Service中包括对很多mapper方法的调用,当我们执行两次Service的调用,查询相同用户的信息,就不会走一级缓存,因为第一个Service方法执行完毕之后,该方法的sqlSession已经被清空了,所以无法调用一级缓存;


二级缓存MyBatis缓存机制的设计与实现和一级缓存的区别在于:二级缓存需要我们手动的开启;首先我们在开启二级缓存之前测试一下效果:

MyBatis缓存机制的设计与实现

 执行完上述代码之后,我们可以发现执行结果如下:


MyBatis缓存机制的设计与实现

 从上面的结果可以看出,我们没有开启之前,mybatis和数据库有两次交互的过程,也就是说默认情况下,mybatis的二级缓存是关闭的,需要我们手动设置。


开启二级缓存

(1)在核心配置文件SqlMapConfig.xml中加入以下内容(开启二级缓存总开关):

cacheEnabled设置为 true

MyBatis缓存机制的设计与实现

 

 

(2)在映射文件中,加入以下内容,开启二级缓存:

MyBatis缓存机制的设计与实现

 

实现序列化

    

由于二级缓存的数据不一定都是存储到内存中,它的存储介质多种多样,所以需要给缓存的对象执行序列化。


如果该类存在父类,那么父类也要实现序列化。

MyBatis缓存机制的设计与实现

禁用二级缓存

该statement中设置userCache=false可以禁用当前select语句的二级缓存,即每次查询都是去数据库中查询,默认情况下是true,即该statement使用二级缓存。

MyBatis缓存机制的设计与实现

刷新二级缓存

MyBatis缓存机制的设计与实现


需要注意的是,使用二级缓存的时候,我们必须使用的是两个不同的session,并且只有在第一个缓存被提交的情况下才能使用二级缓存;


自定义缓存


EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider。


MyBatis定义了Cache接口方便我们进行自定义扩展。

步骤:

(1)导入ehcache包,以及整合包,日志包

MyBatis缓存机制的设计与实现

(2)编写ehcache.xml配置文件
(3)配置cache标签

MyBatis缓存机制的设计与实现

若想在命名空间中共享相同的缓存配置和实例。可以使用 cache-ref 元素来引用另外一个缓存。


缓存相关属性

(1)eviction=“FIFO”:缓存回收策略:• 默认的是 LRU。

  1. LRU – 最近最少使用的:移除最长时间不被使用的对象。

  2. FIFO – 先进先出:按对象进入缓存的顺序来移除它们。

  3. SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。

  4. WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。

(2)flushInterval:刷新间隔,单位毫秒

  默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新

(3)size:引用数目,正整数

  代表缓存最多可以存储多少个对象,太大容易导致内存溢出

(4)readOnly:只读,true/false

  1. true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。

  2. false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是 false。

缓存相关设置

(1)全局setting的cacheEnable:

配置二级缓存的开关。一级缓存一直是打开的。

(2)select标签的useCache属性:

  配置这个select是否使用二级缓存。一级缓存一直是使用的

(3)sql标签的flushCache属性:

  1. 增删改默认flushCache=true。sql执行以后,会同时清空一级和二级缓存。

  2. 查询默认flushCache=false。

(4)sqlSession.clearCache():

   只是用来清除一级缓存。

(5)当在某一个作用域 (一级缓存Session/二级缓存Namespaces) 进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被clear。


总结

 二级缓存与一级缓存区别,二级缓存的范围更大,多个sqlSession可以共享一个UserMapper的二级缓存区域。UserMapper有一个二级缓存区域(按namespace分) ,其它mapper也有自己的二级缓存区域(按namespace分)。每一个namespace的mapper都有一个二缓存区域,两个mapper的namespace如果相同,这两个mapper执行sql查询到数据将存在相同 的二级缓存区域中。


以上是关于MyBatis缓存机制的设计与实现的主要内容,如果未能解决你的问题,请参考以下文章

干货分享 | MyBatis实战缓存机制设计与原理解析

MyBatis缓存机制-二级缓存

Mybatis的缓存机制详解

MyBatis 缓存机制分析

Mybatis——缓存机制

mybatic进阶遗留