Mybatis缓存体系:二级缓存

Mybatis缓存体系:二级缓存

一、概要

前面Mybatis是应用级别的缓存,执行查询是先查询二级,没有命中才查询一级缓存,否则查询数据库。

值得注意的是,不同于一级缓存,因为二级缓存是作用于应用的,那么就存在一个脏读的问题,所以就引入了事务缓存。

二、使用

在Mapper映射文件里配置<cache></cache>即可,那么整个mapper下就会启用二级缓存。也可以粒度控制到方法,比如在配置useCache为false,那么该方法就不会使用二级缓存。另外二级缓存是作用与nameSpace的,如果是关联查询,需要另一个namespece的二级缓存,可以<cache-ref namespace="xxx.xxxMapper"/>。

三、命中条件

  • 相同的statement id
  • 相同的Sql与参数
  • 没有使用ResultHandler来自定义返回数据
  • 没有配置UseCache=false 来关闭缓存
  • 没有配置FlushCache=true 来清空缓存在

四、源码解读

概念介绍

事物缓存管理器(TransactionalCacheManager)

二级缓存是在事务提交后才会写入,目的是为了防止其它会话脏读缓存。所以在话与二级缓存中间会有一个事物缓存管理器,会话其间查询的数据会放到管理器的暂存区。当事务提交后会才会写入指定二级缓存区域。管理器的生命周期与会话保持一至。

public class TransactionalCacheManager {

  private final Map<Cache, TransactionalCache> transactionalCaches = new HashMap<>();

  public void clear(Cache cache) {
    getTransactionalCache(cache).clear();
  }

  public Object getObject(Cache cache, CacheKey key) {
    return getTransactionalCache(cache).getObject(key);
  }
   //放到暂存区
  public void putObject(Cache cache, CacheKey key, Object value) {
    getTransactionalCache(cache).putObject(key, value);
  }
  //提交到二级缓存
  public void commit() {
    for (TransactionalCache txCache : transactionalCaches.values()) {
      txCache.commit();
    }
  }

  public void rollback() {
    for (TransactionalCache txCache : transactionalCaches.values()) {
      txCache.rollback();
    }
  }

  private TransactionalCache getTransactionalCache(Cache cache) {
    return transactionalCaches.computeIfAbsent(cache, TransactionalCache::new);
  }
}

暂存区(TransactionalCache)

暂时存放待缓存的数据区域,和缓存区是一一对应的。如果会话会涉及多个二级缓存的访问,那么对应暂存区也会有多个。暂存区生命周期与会话保持一至。

public class TransactionalCache implements Cache {

  private static final Log log = LogFactory.getLog(TransactionalCache.class);

  private final Cache delegate;
  private boolean clearOnCommit;
  private final Map<Object, Object> entriesToAddOnCommit;
  private final Set<Object> entriesMissedInCache;

  public TransactionalCache(Cache delegate) {
    this.delegate = delegate;
    this.clearOnCommit = false;
    this.entriesToAddOnCommit = new HashMap<>();
    this.entriesMissedInCache = new HashSet<>();
  }
..............................................
..............................................
}

缓存区

缓存区是通过Mapper声明而获得,默认每个Mapper都有独立的缓存区。其作用是真正存放数据和实现缓存的业务逻辑。如序列化、防止缓存穿透、缓存效期等。

执行流程

核心是CachingExecutor这个类(维护了二级缓存),它是对BaseExecutor(维护一级缓存)的装饰,执行sql时候,会先去CachingExecutor查询是否有二级缓存,如果没有再去BaseExecutor查询一级缓存。

接着我们就看.CachingExecutor#query()方法:

可以看到这里实际是放入到暂存区,并没有提交数据到二级缓存。

那么什么时候提交到二级缓存呢?
我们猜想一下应该是会话结束才提交事务,会话关闭就是DefaultSqlSession#close()方法,我们去验证一下:

又跳转到了CachingExecutor#close方法:

可以看到这里提交或者回滚了,验证是对的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值