准备系列-Mybatis(十七) TKmybatis 二级缓存清除策略及工作原理

98 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 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共享, 所以他们的执行顺序就是 二级缓存-> 一级缓存->数据库

image.png

在 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);
}

一步一步的分析 看下

  1. Cache cache = ms.getCache(); 获取缓存
  2. Cache不存在 直接走查询 list = delegate. query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  3. Cache 存在,判断 是否需要清除刷新缓存 flushCacheIfRequired
  4. ms.isUseCache() && resultHandler == null 判断 是否配置使用缓存
  5. resultHandler 是 默认的 Executor.NO_RESULT_HANDLER public interface Executor { ResultHandler NO_RESULT_HANDLER = null;} 默认的就为空
  6. ensureNoOutParams 确保方法没有Out类型的参数,mybatis不支持存储过程的缓存
  7. List list = (List) tcm.getObject(cache, key); tcm就是TransactionalCacheManager,从 transactionCacheManager中根据Key获取缓存
  8. if (list == null) {} 判断缓存是否为空, 不为空,直接返回数据
  9. 为空 直接走查询 list = delegate. query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

至此二级缓存的 工作原理及CachingExecutor的底层逻辑已经分析完毕, 我们可以更好的使用二级缓存了