mybatis二级缓存

简述

  • 定义 二级缓存也称应用级缓存,与一级缓存不同的是它的作用域是整个应用.而且可以跨线程使用,所以二级缓存有更高的命中率,适合缓存修改较少的数据.
  • 存储 内存,硬盘,第三方集成(Redis)
  • 溢出淘汰策略 fifo,lru(默认)
  • 线程安全,线程共享,跨session
  • 顶层接口 org.apache.ibatis.cache.Cache,查询时采用[装饰者+责任链]模式,下图是调用链mybatis二级缓存_第1张图片
  1. SynchronizedCache:同步Cache,实现比较简单,直接使用synchronized修饰方法。
  2. LoggingCache:日志功能,装饰类,用于记录缓存的命中率,如果开启了DEBUG模式,则会输出命中率日志。
  3. SerializedCache:序列化功能,将值序列化后存到缓存中。该功能用于缓存返回一份实例的Copy,用于保存线程安全。
  4. LruCache:采用了Lru算法的Cache实现,移除最近最少使用的Key/Value。
  5. PerpetualCache: 作为为最基础的缓存类,底层实现比较简单,直接使用了HashMap。
  • 配置

mybatis二级缓存_第2张图片

 流程

mybatis二级缓存_第3张图片

 CachingExecutor的query()方法

public  List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    //获得该MappedStatement的cache
    Cache cache = ms.getCache();
    //如果缓存不为空
    if (cache != null) {
        //看是否需要清除cache(在xml中可以配置flushCache属性决定何时清空cache)
        this.flushCacheIfRequired(ms);
        //若开启了cache且resultHandler 为空
        if (ms.isUseCache() && resultHandler == null) {
            this.ensureNoOutParams(ms, parameterObject, boundSql);
            //从TransactionalCacheManager中取cache
            List list = (List)this.tcm.getObject(cache, key);
            //若取出来list是空的
            if (list == null) {
                //查询数据库
                list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
                //将结果存入cache中
                this.tcm.putObject(cache, key, list);
            }

            return list;
        }
    }
    //如果缓存为空,去查询数据库
    return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

TransactionalCacheManager内部有一个map属性

private final Map transactionalCaches = new HashMap();

个人理解:TransactionalCacheManager作为暂存区管理器(事务缓存管理器),在没有经过commit的情况下,修改的数据会暂时存放在这里,这时查询缓存直接查缓存区,是查不到未提交的数据的.当commit后,会将暂存区的数据推送到缓存区中,这时再查询是可以查询的到的.

TransactionalCacheManager暂存区管理器内管理了很多个暂存区,每个暂存区对应相应的缓存区,putObject()方法

public void putObject(Cache cache, CacheKey key, Object value) {
        this.getTransactionalCache(cache).putObject(key, value);
    }

第一个参数就是暂存区的对应关系,找对应的暂存区去修改缓存区.所以所有修改缓存区第一步都是修改暂存区的数据,只有commit方法才是把数据推送到缓存区去

mybatis二级缓存_第4张图片

 

总结[聊聊MyBatis缓存机制 - 美团技术团队]

  • MyBatis的二级缓存相对于一级缓存来说,实现了SqlSession之间缓存数据的共享,同时粒度更加的细,能够到namespace级别,通过Cache接口实现类不同的组合,对Cache的可控性也更强。
  • MyBatis在多表查询时,极大可能会出现脏数据,有设计上的缺陷,安全使用二级缓存的条件比较苛刻。
  • 在分布式环境下,由于默认的MyBatis Cache实现都是基于本地的,分布式环境下必然会出现读取到脏数据,需要使用集中式缓存将MyBatis的Cache接口实现,有一定的开发成本,直接使用Redis、Memcached等分布式缓存可能成本更低,安全性也更高。

问题

  • 为什么提交之后才能命中缓存

要跨线程使用,要保证线程安全

  • TransactionalCache的生命周期

与会话session一致,

备注:图片由鲁班大叔视频截屏得到的

你可能感兴趣的:(mybatis,java)