mybatis缓存机制

Mybatis提供一级缓存,二级缓存和自定义缓存:

mybatis缓存机制


一级缓存是SqlSession级别,其最大的共享范围就是一个SqlSession内部,如果多个SqlSession之间需要共享缓存,则需要使用到二级缓存。默认情况下,mybatis 的一级缓存是开启的。

二级缓存是mapper级别的缓存,对于mapper级别的缓存不同的sqlsession是可以共享的。

一.一级缓存

 如下图所示,MyBatis会在一个SqlSession对象中创建一个本地缓存(local cache),对于每一次查询,都会尝试根据查询的条件去本地缓存中查找是否在缓存中,如果在缓存中,就直接从缓存中取出,然后返回给用户,从而减少资源浪费;否则,从数据库读取数据,将查询结果存入缓存并返回给用户,session关闭,缓存将被清空。

mybatis缓存机制

Mybatis一级缓存默认开启,无需配置。

测试案例1

mybatis缓存机制

运行结果:

可以看到同一个sqlSession两次查询只是发出了一条sql语句,第二次查询是从缓存中读取。

第二个newSqlSession查询的时候也是发出了sql语句,因为缓存已经被清空了,由此可见mybatis一级缓存的sqlsession是不共享的

mybatis缓存机制

测试案例2

mybatis缓存机制

测试结果:

第一步 查询发起sql语句,并将查询结果放入缓存,

第二步对该语句进行更新操作,缓存将被清空

第三步再次查询信息,该语句又发起一次sql语句。

mybatis缓存机制

二.二级缓存

二级缓存可以使用MyBatis自己定义的二级缓存实现;也可以通过实现Cache接口自定义缓存(使用第三方内存缓存库,如redis)。

1.   MyBatis自定义的二级缓存工作机制:

mybatis缓存机制

a).特点:即松散的Cache缓存管理和维护

一个Mapper中定义的增删改查操作只能影响到自己关联的Cache对象,如图所示namespace1产生的缓存只会被放置到相应关联的Cache1中,即Mappernamespace2,namespace3,namespace4 中的CRUD的语句不会影响到Cache1。

b).Mapper之间的缓存关系比较松散,相互关联的程度比较弱

如果将AMapper和BMapper共用一个Cache对象,当BMapper执行更新操作时,可以清空对应Cache中的所有的缓存数据,使缓存使用效率变的很低!AMapper和BMapper的任意的更新操作都会将共用的Cache清空,会频繁地清空Cache,导致Cache实际的命中率和使用率就会变得很低,这种策略实际情况下是不可取的。

相关配置:

SqlSessionFactory层面上的二级缓存是不开启的,二级缓存开启需要配置。

1.使用注解@CacheNamespace方式配置,不写type就使用mybatis默认的缓存,也可以去实现 Cache 接口来自定义缓存
   

@CacheNamespace注解详情:

Implementation: 实现缓存功能的类
PerpetualCache这个类是mybatis默认实现二级缓存功能的类
Eviction:缓存回收策略(可选值有"LRU"、"FIFO"、"SOFT"、"WEAK",默认值是LRU
LruCache:最近最少使用,移除最长时间不被使用的对象.

FIFO:先进先出,按对象进入缓存的顺序来移除

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

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

flushInterval: 指缓存过期时间,单位为毫秒.
默认为0,即只要容量足够,永不过期
Size:指缓存多少个对象
默认值为1024
readWrite:可读/可写
默认为true
Bloking:锁
默认为false
2.对于MyBatis自带二级缓存,实体类可以不用实现序列化接口。

测试案例1:

mybatis缓存机制

测试结果:

创建两个sqlSession对象,

第一次查询发出sql语句,将查询结果翻入缓存中,关闭第一个sqlsession,用第二个sqlsession发起查询,数据是从缓存中读取的,由此可见mybatis二级缓存sqlSession之间是共享的.

mybatis缓存机制

测试案例2:

mybatis缓存机制

测试结果:

第一步sqlSessionOne进行sql查询操作,将查询结果放入缓存中

第二步sqlSessionTwo执行更新操作,缓存将被清空

第三步sqlSessionThree进行查询操作,重新发起sql查询。

执行更新操作,二级缓存数据将被清空

mybatis缓存机制

三.自定义缓存Redis

为了提高扩展性。MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存,通过重写Cache接口中的方法,将mybatis中默认的缓存空间映射到redis空间中。

相关配置:

自定义类MybatisCache通过重写Cache接口中的方法,自定义缓存.

 mybatis缓存机制

mybatis缓存机制

flashInterval:指缓存过期时间,单位为毫秒,60000即为60秒 

 2.自定义缓存redis有时会将数据缓存到硬盘上需要实现 Serializable 序列化接口

mybatis缓存机制

开启了二级缓存后,还需要将要缓存的pojo实现Serializable接口,为了将缓存数据取出执行反序列化操作,因为二级缓存数据存储介质多种多样,不一定只存在内存中,有可能存在硬盘中,如果我们要再取这个缓存的话,就需要反序列化了。所以mybatis中的pojo需要实现Serializable接口。

测试案例1:

mybatis缓存机制

测试结果:

第一步sqlSessionOne进行sql查询操作,将查询结果放入redis缓存中

 mybatis缓存机制

Redis查看结果,数据已被序列化:

mybatis缓存机制

第二步sqlSessionTwo执行更新操作,缓存将被清空

mybatis缓存机制

Redis查看结果:

mybatis缓存机制

第三步sqlSessionThree进行查询操作,重新发起sql查询。

执行更新操作,二级缓存redis数据将被清空

mybatis缓存机制

打开Redis查看结果:

 mybatis缓存机制

禁用二级缓存使用@Options注解,将useCache属性设置为false

options有个timeout

mybatis如果不指定,默认超时时间是不做限制的,默认值为-1.

以秒为单位,当超出了设置的超时时间时,会抛出SQLTimeoutException

#为什么不用mybatis原生态缓存?

因为mybatis原生态缓存有局限性,mybaits的二级缓存区域是以mapper为单位划分,比如某列表使用了join链接查询,当对其中一个表(做了增删改操作)数据发生变动时,会将cache全部清空,从而倒导致cache实际命中率和使用率变低,这种策略不符合我们的业务场景。

测试案例1,使用mybatis原生态缓存:

mybatis缓存机制

测试结果,多次修改表1,表2数据始终使用sql查询:

mybatis缓存机制

测试案例2,使用redis缓存:

需注释掉cache接口的清空缓存clear()方法,因为每次增删改都清空缓存会消耗缓存的内存资源

mybatis缓存机制

测试结果:

1.     第一次执行联表查询将数据放入到缓存中,后对表1进行更新操作,对表2执行sql查询,发现数据是从缓存中读取。

mybatis缓存机制