MyBatis:缓存机制详解

Mybatis查询缓存分为一级缓存和二级缓存,一级缓存属于sqlsession级别,二级缓存属于Mapper级别。
Mybatis通过缓存机制可减轻直接访问数据库的压力,提高数据库性能。

Mybatis默认开启一级缓存,但与Spring整合后会缓存失效。

  1. 一级缓存是SqlSession级别的缓存,PerpetualCache
  2. 二级缓存是 mapper 级别的缓存。是多个SqlSession共享的。

一级缓存:sqlSession级别

  一级缓存是SqlSession级别的缓存,在操作数据库时需要构造SqlSession对象,在对象中有一个HashMap用于存储缓存数据。不同的SqlSession之间的缓存数据区域HashMap是互不影响的。

  Mybatis缓存机是基于id进行缓存的,也就是说,Mybatis使用HashMap缓存数据时,是使用对象的id作为key,而对象作为value保存的。

  第一次以id为1进行查询执行了一条select语句,第二次获取id为1的数据,不会再执行 select 语句。如果不执行session.commit(),操作没有提交到数据库,此时Mybatis不会清空SqlSession缓存。

  若在第一次查询id为的数据时执行了一条select语句,接下来执行了一个update,delete,insert into操作,Mybatis为了保证缓存中存储的最新信息,会清空SqlSession缓存。

  因一级缓存是SqlSession级别的,如果在执行第一次根据id查询后,执行了close()方法,该方法会关闭SqlSession缓存,第二次根据相同的id查询,一级缓存也就是SqlSession缓存是一个新的对象,会再次执行select语句。

  在实际的项目开发中,往往会将MybatisSpring整合,SqlSession会交给Spring管理,每查询结束后,Spring会清空当前的SqlSession释放资源,也就每次查询所使用的SqlSession是不相同的,导致Mybatis的一级缓存失效

二级缓存:mapper级别

  二级缓存是mapper级别的缓存,是多个SqlSession使用同一个mapper的sql语句去操作数据库,得到的数据会存在二级缓存区域。

  二级缓存也是使用HashMap进行数据存储的,范围比一级缓存更大,是跨SqlSession的,多个SqlSession可以共用二级缓存。

  二级缓存是多个SqlSession共享的,其作用域是mapper的同一个namespace。不同的SqlSession两次执行相同namespace下的sql语句,且向sql中传递的参数也相同,即最终执行相同的sql语句,即第一次执行完毕会将从数据库查询到的数据写入缓存(内存),第二次查询会从缓存中获取数据,不再去底层数据库查询,从而提高查询效率。

  当Mybatis在一级缓存中没有找到与id对应的对象时,就会去二级缓存中查找,如果还没有,就去数据库查找。

  Mybatis默认没有开启二级缓存,需要在setting全局参数中配置开启二级缓存。开启配置如下:
在Mybatis的配置文件mybatis-config.xml配置

1
2
3
4
<settings>
<!-- 开启二级缓存 -->
<setting name="cacheEnabled" value="true"/>
</settings>

  开始当前mapper的namespace下的二级缓存,在Mapper.xml文件增加配置:

1
2
<!-- 例:xxxMapper.xml,创建一个LRU缓存,并每隔60秒刷新,最大储储512个对象,返回对象是只读 -->
<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>

cache开启当前mapper的namespace下的二级缓存。该元素属性设置如下:

  1. flushInterval刷新间隔,单位毫秒,默认不设置没有刷新间隔,在调用时刷新。
  2. size缓存数目,需要缓存的对象数目,默认值是1024。
  3. readOnly只读,可设置为true或false。默认false,可读写,返回的是缓存对象的拷贝(通过序列化),会慢些,但安全。

eviction的策略:

  1. LRU:最近最少使用的策略,移除最长时间不被使用的对象。
  2. FIFO:先进先出策略,按对象进入缓存的存序来移除它他。
  3. SOFT:软引用策略,移除基于垃圾回收器状态和软引用规则的对象。
  4. WEAK:弱引用策略,更积极地移除基于垃圾收集器状态和弱引用规则的对象。

  使用二级缓存时,与查询结果映射的Java对象,必须实现序列化接口:java.io.Serializable。如果存在父类,其父类成员都需要实现序列化接口。

  实现序列化接口是为了对缓存数据进行序列化和反序列化操作,因为二级缓存数据存储介质多样,不一定在内存,有可能是硬盘或远程服务器。

  在select中设置useCache=false,可以禁用当前select语句的二级缓存,即每次查询都会发出sql查询,默认是true即开启二级缓存,则该sql会使用二级缓存。禁用缓存设置通常用于每次查询都需要最新的数据情况。

  当一级缓存中没有数据时,若开启了二级缓存,则会到二级缓存中查找。

作者

光星

发布于

2017-12-30

更新于

2022-06-17

许可协议

评论