Mybaits基础缓存

Posted 烟锁迷城

tags:

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

1、缓存的设置与使用

在Mybaits的jar包中,所有的缓存都需要实现一个接口org.apache.ibatis.cache.Cache,它具有最基础的一个实现类PerpetualCache。除此之外,Mybaits还使用了装饰器模式,在decorators文件夹内有一些装饰器,在不改变原有对象的基础上,增强扩展其功能。

缓存实现类功能说明
缓存实现类描述作用装饰条件
基本缓存缓存基本实现类默认是PerpetualCache,也可以自定义如RedisCache,EhCache等,具备基本功能的缓存类
LruCacheLRU策略的缓存当缓存达到上限时,删除最近最少使用的缓存eviction=“LRU”(默认)
FifoCacheFIFO策略的缓存当缓存达到上限时,删掉最先入队的缓存eviction=“FIFO”

SoftReference

WeakReference

待清理策略的缓存通过JVM的软引用和弱引用来实现缓存,当JVM内存不足时,会自动清理掉这些缓存,基于SoftReference和WeakReference

eviction=“SOFT”

eviction=“WEAK”

LoggingCache带日志功能的缓存比如输出缓存命中率基本
SynchronizedCache同步缓存基于Synchronized关键字实现,解决并发问题基本
BlockingCache阻塞缓存通过get/put方式中加锁,保证只有一个线程操作缓存,基于java重入锁实现block=true
SeralizedCache支持序列化的缓存将对象序列化后存到缓存中,取出时反序列化readOnly=false
ScheduledCache定时调度的缓存在进行get/put/remove/getSize等操作前,判断缓存时间是否超过了设置的最长缓存时间(默认是1小时),如果是则清空缓存,即每隔一段时间清空一次缓存<mapper>的<cache>标签的flushInterval不为空
TransactionalCache事务缓存在二级缓存中使用,可一次存入多个缓存,移除多个缓存在TransactionCacheManager中用Map维护对应关系。

这些装饰类的使用是在对应的表Mapper文件中,使用cache标签内部的参数进行设置。 

<cache type="org.apache.ibatis.cache.impl.PerpetualCache" 
           size="1024" eviction="LRU" flushInterval="120000" readOnly="false"/>

2、一级缓存

一级缓存又叫本地缓存,默认开启,作用域是session(会话级别),这个session实际上是SQLSession,SQLSession有一个默认实现DefaultSqlSession,在DefaultSqlSession中,关键的属性有两个。

private final Configuration configuration;
private final Executor executor;

configuration是全局唯一的配置,所以只能在一个session中进行使用的一级缓存应该放Executor 中。

那么在BaseExecutor 中确实包含有缓存。

public abstract class BaseExecutor implements Executor {
    private static final Log log = LogFactory.getLog(BaseExecutor.class);
    protected Transaction transaction;
    protected Executor wrapper;
    protected ConcurrentLinkedQueue<BaseExecutor.DeferredLoad> deferredLoads;
    protected PerpetualCache localCache;
    protected PerpetualCache localOutputParameterCache;
    protected Configuration configuration;
    protected int queryStack;
    private boolean closed;

因此,作为SQLSession的一个属性,一级缓存是不能跨session的,只能在一个session内进行读取。

一级缓存有缓存失效的情况,当同一个session内执行更新操作,一级缓存会失效,这时再去读取就会让缓存失效。

一级缓存有脏数据的问题,因为一级缓存不能跨session,所以当一个session有一级缓存,另一个session更新了对应数据后,就会导致第一个session内的缓存不知道数据已经被更新,导致出现脏数据。

除此之外,如果非要关闭一级缓存,可以在settings标签中设置。当localCacheScope设置为Session时代表作用域是session,设置为Statement时代表作用域是statement,也就起不到缓存效果,算是关闭了。

<setting name="localCacheScope" value="STATEMENT"/> //一级缓存不生效
<setting name="localCacheScope" value="SESSION"/> //一级缓存生效

3、二级缓存

二级缓存的作用域是namespace,namespace就是命名空间,mapper文件中有一个标签就是namepace,在这一个mapper文件下的所有语句共享一个命名空间,无论是不是一个SQLSession。

二级缓存的维护对象是CachingExecutor,因为二级缓存的作用范围大于一级缓存,所以查询时需要先命中二级缓存,再命中一级缓存,最后查询数据库。

 在spring-mvc.xml文件的settings标签下,有一个内容,只有cacheEnabled设置为false时,二级缓存才不生效,无论是true还是不设置都是生效的。

<settings>
    <setting name="cacheEnabled" value="true"/>
</settings>

 想要二级缓存生效,还需要在对应的mapper的xml文件中增加标签

<cache type="org.apache.ibatis.cache.impl.PerpetualCache"
           size="1024" eviction="LRU" flushInterval="120000" readOnly="false"/>

在这个标签中,每一个属性都有自己的配置:

  1. type:配置缓存类,除了自带的类,也可以设置第三方的类进行缓存,比如redis,EH
  2. size:缓存管理的key数量
  3. eviction:选用的缓存淘汰策略
  4. flushInterval:缓存对象存活时间
  5. readonly:是否只读。如果只读,则返回的都是同一个类。非只读,就需要实现序列化,因为mybaits会为缓存对象进行序列化和反序列化创造新的类。

这样,在这个namespace中,所有的select的结果都能被二级缓存进行缓存,更新等操作则会清除缓存。如果某一个select的结果不想被缓存,可以在select标签中增加属性

useCache="false"

<select id="queryUser" parameterType="java.lang.Long" 
    resultMap="BaseResultMap" useCache="false">
    select
        <include refid="Base_Column_List" />
    from user
</select>

如果两个namespace想要共用一个二级缓存,可以在其中一个namespace中增加一个标签,cache-ref,这样就可以将共用缓存了。

<cache-ref namespace=""/>

以上是关于Mybaits基础缓存的主要内容,如果未能解决你的问题,请参考以下文章

Mybaits 缓存

Mybaits(13)缓存

MyBaits_查询缓存02_Ehcache二级缓存

springboot+mybaits的二级缓存的案例

mybaits实现分布式缓存

Mybaits 源码解析 ----- 全网最详细,没有之一:一级缓存和二级缓存源码分析