美文网首页
mybatis源码3-二级缓存

mybatis源码3-二级缓存

作者: modou1618 | 来源:发表于2019-01-19 16:03 被阅读0次

一 配置

  • 映射器中cache配置解析
private void cacheElement(XNode context) throws Exception {
  if (context != null) {
    String type = context.getStringAttribute("type", "PERPETUAL");
    Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
    String eviction = context.getStringAttribute("eviction", "LRU");
    Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
    Long flushInterval = context.getLongAttribute("flushInterval");
    Integer size = context.getIntAttribute("size");
    boolean readWrite = !context.getBooleanAttribute("readOnly", false);
    Properties props = context.getChildrenAsProperties();
    builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, props);
  }
}

二 初始化

2.1 cache创建CacheBuilder.build()

  • setDefaultImplementations(); 初始化cache的实现类,
private void setDefaultImplementations() {
  if (implementation == null) {
// implementation表示存储类型,默认PerpetualCache是内存map存储。
    implementation = PerpetualCache.class;
    if (decorators.size() == 0) {
//decorators表示过期策略,默认LruCache是最近最少使用过期策略
      decorators.add(LruCache.class);
    }
  }
}
  • Cache cache = newBaseCacheInstance(implementation, id);初始化缓存类
  • setCacheProperties(cache);配置值按属性注入到缓存对象中

2.1.1 PerpetualCache类型处理

  • 按配置过期策略decorators创建代理类,注入配置
  • clearInterval配置则创建ScheduledCache,周期清理缓存
  • readWrite配置则创建SerializedCache,读写缓存时序列化处理
  • 创建LoggingCache代理纪录命中率
  • 创建SynchronizedCache代理,加锁控制并发
if (PerpetualCache.class.equals(cache.getClass())) { // issue #352, do not apply decorators to custom caches
  for (Class<? extends Cache> decorator : decorators) {
    cache = newCacheDecoratorInstance(decorator, cache);
    setCacheProperties(cache);
  }
  cache = setStandardDecorators(cache);
}

private Cache setStandardDecorators(Cache cache) {
  try {
    MetaObject metaCache = SystemMetaObject.forObject(cache);
    if (size != null && metaCache.hasSetter("size")) {
      metaCache.setValue("size", size);
    }
    if (clearInterval != null) {
      cache = new ScheduledCache(cache);
      ((ScheduledCache) cache).setClearInterval(clearInterval);
    }
    if (readWrite) {
      cache = new SerializedCache(cache);
    }
    cache = new LoggingCache(cache);
    cache = new SynchronizedCache(cache);
    return cache;
  } catch (Exception e) {
    throw new CacheException("Error building standard cache decorators.  Cause: " + e, e);
  }
}

2.1.2 其他类型处理

  • 非LoggingCache类型缓存,则创建LoggingCache代理纪录命中率

2.2 cache缓存

  • configuration.addCache(cache);配置中保存cache
  • mappedStatement.cache也保存cache

2.3 缓存代理

2.3.1 LruCache

  • 使用LinkedHashMap实现,size为缓存最大数量,且为最近使用的缓存。
  • LinkedHashMap是在hashmap存储时,额外使用链表存储对象,每次操作的对象都提取到链表头,表示最近使用
  • 重写removeEldestEntry方法,只保存size数量的缓存数据。
keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {
  private static final long serialVersionUID = 4267176411845948333L;

  protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
    boolean tooBig = size() > size;
    if (tooBig) {
      eldestKey = eldest.getKey();
    }
    return tooBig;
  }
};

2.3.2 ScheduledCache

  • 所有缓存操作前,先检查距离上次处理是否达到周期时间,默认1h
  • System.currentTimeMillis() - lastClear > clearInterval达到周期时间,则清除所有缓存。

2.3.3 ScheduledCache

  • 序列化代理,读写缓存前对对象进行序列化或反序列化处理。非内存缓存时使用。

2.3.4 LoggingCache

  • 获取缓存时,纪录请求次数和成功纪录
  • (double) hits / (double) requests计算缓存命中率

2.3.5 SynchronizedCache

  • 所有缓存操作方法使用synchronized进行并发控制。

三 使用

  • 配置使用二级缓存cacheEnabled,则创建执行器代理CachingExecutor
  • 查询操作时,Cache cache = ms.getCache();获取初始化的缓存
  • 若sql查询配置了flushCacheRequired,则不使用缓存,sql查询前清除缓存
  • 存在缓存则使用,不存在则查询后新增缓存。使用TransactionalCacheManager管理缓存
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, parameterObject, 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. Query must be not synchronized to prevent deadlocks
      }
      return list;
    }
  }
  return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

3.1 TransactionalCacheManager

  • Map<Cache, TransactionalCache> transactionalCaches存储缓存

3.2 TransactionalCache

  • 事务内的缓存数据管理,事务commit前先缓存在TransactionalCache中,提交时再统一向目标cache进行缓存或删除操作。
public void putObject(Object key, Object object) {
  entriesToRemoveOnCommit.remove(key);
  entriesToAddOnCommit.put(key, new AddEntry(delegate, key, object));
}

public Object removeObject(Object key) {
  entriesToAddOnCommit.remove(key);
  entriesToRemoveOnCommit.put(key, new RemoveEntry(delegate, key));
  return delegate.getObject(key);
}
  • entriesToAddOnCommit事务提交时要保存的缓存
  • entriesToRemoveOnCommit事务提交时要删除的缓存
  • 事务提交时,遍历entriesToAddOnCommit和entriesToRemoveOnCommit,对目标缓存进行操作。然后reset()清空
public void commit() {
  if (clearOnCommit) {
    delegate.clear();
  } else {
    for (RemoveEntry entry : entriesToRemoveOnCommit.values()) {
      entry.commit();
    }
  }
  for (AddEntry entry : entriesToAddOnCommit.values()) {
    entry.commit();
  }
  reset();
}
  • 事务回滚时,reset清空数据
private void reset() {
  clearOnCommit = false;
  entriesToRemoveOnCommit.clear();
  entriesToAddOnCommit.clear();
}

相关文章

网友评论

      本文标题:mybatis源码3-二级缓存

      本文链接:https://www.haomeiwen.com/subject/fjuadqtx.html