开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 17 天,点击查看活动详情
上面文章我们介绍了Mybatis的二级缓存及二级缓存的配置和使用, 本篇文章我们着重清除策略及二级缓存的工作原理
1. 二级缓存的清除策略
1.1 缓存的清除策略
- 1.LRU - 最近最久未使用
- 移除最长时间不被使用的对象.
- 2.FIFO - 先进先出
- 按对象进入缓存的顺序来移除它们.
- 3.SOFT - 软引用
- 移除基于垃圾收集器状态和软引用规则的对象.
- 4.WEAK - 弱应用
- 更积极的移除基于垃圾收集器状态和弱引用规则的对象.
1.2 CacheNameSpace类
我们先看一下如何配置CacheNameSpace类
- 默认实现方式是PerpetualCache.class
- PerpetualCache也是mybatis中默认使用的缓存类型,Perpetual的将缓存以HashMap格式进行存放,CachKey作为map的key值,查询结果作为map的value值
- 默认的清除策略是 LruCache.class
- 默认的刷新清空间隔是 flushInterval=0 默认不刷新清空
- 代表间隔多长时间自动清空缓存, 单位毫秒, 600000毫秒=10分钟
- 默认的缓存对象数量是 1024
- blocking-若缓存中找不到对应的key,是否会一直blocking,直到有对应的数据进入缓存
- readWrite 默认设置为true, 代表返回只读缓存, 每次从缓存取出的是缓存对象本身. 这种执行效率较高
- 设置为false, 代表每次取出的是缓存对象的"副本", 每一次取出的对象都是不同的, 这种安全性较高
public @interface CacheNamespace {
Class<? extends org.apache.ibatis.cache.Cache> implementation() default PerpetualCache.class;
Class<? extends org.apache.ibatis.cache.Cache> eviction() default LruCache.class;
long flushInterval() default 0;
int size() default 1024;
boolean readWrite() default true;
boolean blocking() default false;
/**
* Property values for a implementation object.
* @since 3.4.2
*/
Property[] properties() default {};
}
2.二级缓存的原理
二级缓存是基于同一个namespace的, 比如我们UserMapper上标注的 CacheNamespace 接口 也就是说, 同一个namespace的操作语句,影响的是同一个Cache, 二级缓存被多个SqlSession共享, 所以他们的执行顺序就是 二级缓存-> 一级缓存->数据库
在 mybatis 中,使用 Cache 的地方在 CachingExecutor中,来看一下 CachingExecutor 中缓存做了什么工作,我们以查询为例
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
return delegate.<E> query(ms, parameterObject, rowBounds, resultHandl```
resultHandler
```er, key, boundSql);
}
一步一步的分析 看下
- Cache cache = ms.getCache(); 获取缓存
- Cache不存在 直接走查询 list = delegate. query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
- Cache 存在,判断 是否需要清除刷新缓存 flushCacheIfRequired
- ms.isUseCache() && resultHandler == null 判断 是否配置使用缓存
- resultHandler 是 默认的 Executor.NO_RESULT_HANDLER public interface Executor { ResultHandler NO_RESULT_HANDLER = null;} 默认的就为空
- ensureNoOutParams 确保方法没有Out类型的参数,mybatis不支持存储过程的缓存
- List list = (List) tcm.getObject(cache, key); tcm就是TransactionalCacheManager,从 transactionCacheManager中根据Key获取缓存
- if (list == null) {} 判断缓存是否为空, 不为空,直接返回数据
- 为空 直接走查询 list = delegate. query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
至此二级缓存的 工作原理及CachingExecutor的底层逻辑已经分析完毕, 我们可以更好的使用二级缓存了