前言
MyBatis的二级缓存是Application级别的缓存,它可以提高对数据库查询的效率,以提高应用的性能。
一. MyBatis的缓存机制整体设计以及二级缓存的工作模式
MyBatis缓存机制示意图,图片来源https://blog.csdn.net/luanlouis/article/details/41408341当开一个会话时,一个SqlSession对象会使用一个Executor对象来完成会话操作,MyBatis的二级缓存机制的关键就是对这个Executor对象做文章。
如果用户配置了"cacheEnabled=true",那么MyBatis在为SqlSession对象创建Executor对象时,会对Executor对象加上一个装饰者:
CachingExecutor,这时SqlSession使用CachingExecutor对象来完成操作请求。
CachingExecutor对于查询请求,会先判断该查询请求在Application级别的二级缓存中是否有缓存结果,如果有查询结果,则直接返回缓存结果;如果缓存中没有,再交给真正的Executor对象来完成查询操作,之后CachingExecutor会将真正Executor返回的查询结果放置到缓存中,然后在返回给用户.
MyBatis并不是简单地对整个Application就只有一个Cache缓存对象,它将缓存划分的更细,即是Mapper级别的,即每一个Mapper都可以拥有一个Cache对象,具体如下:
a.为每一个Mapper分配一个Cache缓存对象(使用<cache>节点配置);
b.多个Mapper共用一个Cache缓存对象(使用<cache-ref>节点配置);
如果你想让多个Mapper公用一个Cache的话,你可以使用<cache-ref namespace="">节点,来指定你的这个Mapper使用到了哪一个Mapper的Cache缓存。
二. 二级缓存配置
要正确的使用二级缓存,需完成如下配置的。
1 在Mybatis的配置文件中开启二级缓存。
<setting name="cacheEnabled" value="true"/>
2 在Mybatis的映射XML中配置cache或者 cache-ref 。
<cache/>
cache标签用于声明这个namespace使用二级缓存,并且可以自定义配置。可以配置的属性如下:
type: cache使用的类型,默认是PerpetualCache,这在一级缓存中提到过。
eviction: 定义回收的策略,常见的有FIFO,LRU。
flushInterval: 配置一定时间自动刷新缓存,单位是毫秒
size: 最多缓存对象的个数
readOnly: 是否只读,若配置可读写,则需要对应的实体类能够序列化。
blocking: 若缓存中找不到对应的key,是否会一直blocking,直到有对应的数据进入缓存。
cache-ref代表引用别的命名空间的Cache配置,两个命名空间的操作使用的是同一个Cache。
<cache-ref namespace="mapper.StudentMapper" />
三. 一级缓存和二级缓存的使用顺序
请注意,如果你的MyBatis使用了二级缓存,并且你的Mapper和select语句也配置使用了二级缓存,那么在执行select查询的时候,MyBatis会先从二级缓存中取输入,其次才是一级缓存,即MyBatis查询数据的顺序是:
二级缓存 ———> 一级缓存 ——> 数据库
四. 二级缓存实现的选择
MyBatis对二级缓存的设计非常灵活,它自己内部实现了一系列的Cache缓存实现类,并提供了各种缓存刷新策略如LRU,FIFO等等;另外,MyBatis还允许用户自定义Cache接口实现,用户是需要实现org.apache.ibatis.cache.Cache接口,然后将Cache实现类配置在<cache type="">节点的type属性上即可;除此之外,MyBatis还支持跟第三方内存缓存库如Memecached的集成,总之,使用MyBatis的二级缓存有三个选择:
1.MyBatis自身提供的缓存实现;
2. 用户自定义的Cache接口实现;
3.跟第三方内存缓存库的集成;
五.Cache实现类的介绍
Cache实现类的组合为Cache赋予了不同的能力。
SynchronizedCache: 同步Cache,实现比较简单,直接使用synchronized修饰方法。
LoggingCache: 日志功能,装饰类,用于记录缓存的命中率,如果开启了DEBUG模式,则会输出命中率日志。
SerializedCache: 序列化功能,将值序列化后存到缓存中。该功能用于缓存返回一份实例的Copy,用于保存线程安全。
LruCache: 采用了Lru算法的Cache实现,移除最近最少使用的key/value。
PerpetualCache: 作为为最基础的缓存类,底层实现比较简单,直接使用了HashMap。
六. 二级缓存注意的点
1.当sqlsession没有调用commit()或者close()方法时,二级缓存不会起到作用。
2.update操作会刷新该namespace下的二级缓存。
3.Mybatis的二级缓存不适应用于映射文件中存在多表查询的情况。
由于Mybatis的二级缓存是基于namespace的,多表查询语句所在的namspace无法感应到其他namespace中的语句对多表查询中涉及的表进行了修改,引发脏数据问题。
4.为了解决第3点的问题,可以使用Cache ref,让ClassMapper引用StudenMapper命名空间,这样两个映射文件对应的Sql操作都使用的是同一块缓存了。不过这样做的后果是,缓存的粒度变粗了,多个Mapper namespace下的所有操作都会对缓存使用造成影响,
七.总结
Mybatis的二级缓存相对于一级缓存来说,实现了SqlSession之间缓存数据的共享,同时粒度更加的细,能够到Mapper级别,通过Cache接口实现类不同的组合,对Cache的可控性也更强。
Mybatis在多表查询时,极大可能会出现脏数据,有设计上的缺陷,安全使用的条件比较苛刻。
在分布式环境下,由于默认的Mybatis Cache实现都是基于本地的,分布式环境下必然会出现读取到脏数据,需要使用集中式缓存将Mybatis的Cache接口实现,有一定的开发成本,不如直接用Redis,Memcache实现业务上的缓存就好了。
最终的结论是Mybatis的缓存机制设计的不是很完善,在使用上容易引起脏数据问题,建议不要使用Mybatis缓存,在业务层面上使用其他机制实现需要的缓存功能,
网友评论