Mybatis源码系列5-二级缓存(上)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: Mybatis源码系列5-二级缓存(上)

2020年的Flag已经立完,不知道靠不靠谱,但是挡不住对未来美好的向往

启用二级缓存二级缓存的位置二级缓存的样子二级缓存的工作原理装饰器模式事务型预缓存二级缓存的刷新总结:

Mybatis 的二级缓存相比一级缓存就复杂的多了,如果用一句话来说明Mybatis的二级缓存:

二级缓存是一个全局性,事务性,多样性的缓存

那问题来了:

二级缓存在哪里?

二级缓存长什么样子?

全局性,事务性,多样性如何体现?

工作原理是怎么样的呢?

来一探究竟


启用二级缓存


分为三步走:

1)开启全局二级缓存配置:

2)  在需要使用二级缓存的Mapper配置文件中配置二级缓存类型

  • 为每一个Mapper分配一个Cache缓存对象(使用节点配置)
  • 多个Mapper共用一个Cache缓存对象(使用节点配置);

3)在具体CURD标签上配置 useCache=true


二级缓存的位置


上文开启二级缓存步骤中,可以看出,二级缓存的配置是在xml文件中。所以想要探究二级缓存在哪里。还是得从xml文件的解析过程入手。

在[xml文件的解析]()一文讲过,Mapper配置文件的解析是由XMLMapperBuilder 解析器解析的

//-----------XMLMapperBuilder类
private void configurationElement(XNode context) {
    try {
      ...
      //解析cache标签
      cacheRefElement(context.evalNode("cache-ref"));
      cacheElement(context.evalNode("cache"));
      ...
    } catch (Exception e) {
    }
}
//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);
      boolean blocking = context.getBooleanAttribute("blocking", false);
      Properties props = context.getChildrenAsProperties();
        //构建助手帮助创建Cache
      builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
    }
  }
//-----------MapperBuilderAssistant类
public Cache useNewCache(...) {
    Cache cache = new CacheBuilder(currentNamespace)
        .implementation(valueOrDefault(typeClass, PerpetualCache.class))
        .addDecorator(valueOrDefault(evictionClass, LruCache.class))
        .clearInterval(flushInterval)
        .size(size)
        .readWrite(readWrite)
        .blocking(blocking)
        .properties(props)
        .build();
    configuration.addCache(cache);//添加到configuration一份
    currentCache = cache;//设置到当前临时变量
    return cache;
  }
public MappedStatement addMappedStatement(....){
    MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType).....cache(currentCache);//设置临时缓存变量
    MappedStatement statement = statementBuilder.build();//创建MappedStatement
    configuration.addMappedStatement(statement);
    return statement;
}

可以看出:

  • 二级缓存就是每一个SQL模板MappedStatement 实例的cache属性,他部署
  • 同一个namespace下的所有MappedStatement.cache属性 指向同一个cache对象。共用一个二级缓存


二级缓存的样子


二级缓存具有多样性,我们可以根据需求配置不同类型的二级缓存。

有哪些呢?

大类 类型 缓存名称 描述
基础实现 基础类 PerpetualCache 基础缓存,本质是包装了HashMap
装饰类 算法类 FifoCache 先进先出缓存
LruCache 最近最少使用
引用类 SoftCache 软引用,内存不够发生GC时删除
WeakCache 弱引用,发生GC就回收
技术增强类 SerializedCache 将缓存对象在保存前序列化和获取后反序列化
SynchronizedCache 对缓存的所有方法都加上synchronized
业务增强类 LoggingCache 记录缓存的日志,比如什么时候进来的,什么时候被删除的
TransactionalCache 事务性缓存
ScheduledCache 定期删除缓存
BlockingCache 缓存阻塞

可以看出二级缓存的种类很多。mybatis是如何组织二级缓存的呢?

重点就在参数配置上,

参数 描述
type 缓存底层实现,默认是PerpetualCache
eviction 清除策略 LRU、FIFO
flushInterval 刷新间隔,ScheduledCache
size 缓存的大小
readWrite 缓存的读写
blocking 当缓存key不存在时,是否直接查询数据库。默认false

参数的不同直接影响了二级缓存的样子

//根据属性配置来构建cache
Cache cache = new CacheBuilder(currentNamespace)
        .implementation(valueOrDefault(typeClass, PerpetualCache.class))
        .addDecorator(valueOrDefault(evictionClass, LruCache.class))
        .clearInterval(flushInterval)
        .size(size)
        .readWrite(readWrite)
        .blocking(blocking)
        .properties(props)
        .build();
  //build方法
 public Cache build() {
    setDefaultImplementations();
    Cache cache = newBaseCacheInstance(implementation, id);
    setCacheProperties(cache);
    if (PerpetualCache.class.equals(cache.getClass())) {
      for (Class<? extends Cache> decorator : decorators) {
        cache = newCacheDecoratorInstance(decorator, cache);
        setCacheProperties(cache);
      }
      cache = setStandardDecorators(cache);
    } else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {
      cache = new LoggingCache(cache);
    }
    return 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) {
      //如果配置了清理时间,使用ScheduledCache装饰
        cache = new ScheduledCache(cache);
        ((ScheduledCache) cache).setClearInterval(clearInterval);
      }
      if (readWrite) {
      //如果配置了读写,使用SerializedCache装饰
        cache = new SerializedCache(cache);
      }
      //使用LoggingCache装饰
      cache = new LoggingCache(cache);
      //使用SynchronizedCache 装饰
      cache = new SynchronizedCache(cache);
      if (blocking) {
      //如果配置阻塞,使用BlockingCache装饰
        cache = new BlockingCache(cache);
      }
      return cache;
    } catch (Exception e) {
      throw new CacheException("Error building standard cache decorators.  Cause: " + e, e);
    }
  }

二级缓存采用装饰器模式来设计。通过不同的配置,使用不同功能的缓存装饰器来装饰基础缓存,使基础缓存具有特殊的功能。

也就是说:

二级缓存= 多级装饰器+ 基础缓存类


二级缓存的工作原理


说到二级缓存的工作原理,可以用两个知识点来总结

  • 装饰器
  • 事务型预缓存


装饰器模式

CachingExecutor

在创建DefaultSqlSession的执行器Executor时,如果开启了二级缓存功能,会创建一个装饰器CachingExecutor,来装饰基础Executor。

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    ...
    if (cacheEnabled) {
    //二级缓存装饰器
      executor = new CachingExecutor(executor);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }


相关文章
|
4月前
|
缓存 Java 数据库连接
mybatis复习05,mybatis的缓存机制(一级缓存和二级缓存及第三方缓存)
文章介绍了MyBatis的缓存机制,包括一级缓存和二级缓存的配置和使用,以及如何整合第三方缓存EHCache。详细解释了一级缓存的生命周期、二级缓存的开启条件和配置属性,以及如何通过ehcache.xml配置文件和logback.xml日志配置文件来实现EHCache的整合。
mybatis复习05,mybatis的缓存机制(一级缓存和二级缓存及第三方缓存)
|
4月前
|
SQL XML Java
mybatis-源码深入分析(一)
mybatis-源码深入分析(一)
|
13天前
|
缓存 NoSQL Java
Mybatis学习:Mybatis缓存配置
MyBatis缓存配置包括一级缓存(事务级)、二级缓存(应用级)和三级缓存(如Redis,跨JVM)。一级缓存自动启用,二级缓存需在`mybatis-config.xml`中开启并配置映射文件或注解。集成Redis缓存时,需添加依赖、配置Redis参数并在映射文件中指定缓存类型。适用于查询为主的场景,减少增删改操作,适合单表操作且表间关联较少的业务。
|
1月前
|
缓存 Java 数据库连接
MyBatis缓存机制
MyBatis提供两级缓存机制:一级缓存(Local Cache)默认开启,作用范围为SqlSession,重复查询时直接从缓存读取;二级缓存(Second Level Cache)需手动开启,作用于Mapper级别,支持跨SqlSession共享数据,减少数据库访问,提升性能。
37 1
|
1月前
|
缓存 Java 数据库连接
深入探讨:Spring与MyBatis中的连接池与缓存机制
Spring 与 MyBatis 提供了强大的连接池和缓存机制,通过合理配置和使用这些机制,可以显著提升应用的性能和可扩展性。连接池通过复用数据库连接减少了连接创建和销毁的开销,而 MyBatis 的一级缓存和二级缓存则通过缓存查询结果减少了数据库访问次数。在实际应用中,结合具体的业务需求和系统架构,优化连接池和缓存的配置,是提升系统性能的重要手段。
94 4
|
2月前
|
SQL 缓存 Java
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
本文详细介绍了MyBatis的各种常见用法MyBatis多级缓存、逆向工程、分页插件 包括获取参数值和结果的各种情况、自定义映射resultMap、动态SQL
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
|
2月前
|
SQL 缓存 Java
MyBatis如何关闭一级缓存(分注解和xml两种方式)
MyBatis如何关闭一级缓存(分注解和xml两种方式)
98 5
|
4月前
|
缓存 Java 开发工具
Spring是如何解决循环依赖的?从底层源码入手,详细解读Spring框架的三级缓存
三级缓存是Spring框架里,一个经典的技术点,它很好地解决了循环依赖的问题,也是很多面试中会被问到的问题,本文从源码入手,详细剖析Spring三级缓存的来龙去脉。
240 24
|
3月前
|
缓存 Java 数据库连接
使用MyBatis缓存的简单案例
MyBatis 是一种流行的持久层框架,支持自定义 SQL 执行、映射及复杂查询。本文介绍了如何在 Spring Boot 项目中集成 MyBatis 并实现一级和二级缓存,以提高查询性能,减少数据库访问。通过具体的电商系统案例,详细讲解了项目搭建、缓存配置、实体类创建、Mapper 编写、Service 层实现及缓存测试等步骤。
|
3月前
|
缓存 NoSQL Ubuntu
大数据-39 Redis 高并发分布式缓存 Ubuntu源码编译安装 云服务器 启动并测试 redis-server redis-cli
大数据-39 Redis 高并发分布式缓存 Ubuntu源码编译安装 云服务器 启动并测试 redis-server redis-cli
73 3