干翻Mybatis源码系列之第八篇:Mybatis提供的缓存方案细节注意

简介: 干翻Mybatis源码系列之第八篇:Mybatis提供的缓存方案细节注意

前言

一级缓存方案

Mybatis缓存设计成了两层的体系,第一层叫做一级缓存,第二层叫做二级缓存(全局缓存)。我们从这里可以看到Mybatis的缓存方案是有两种处理方式的。

一级缓存(默认开启)

一级缓存默认开启的,程序首先去缓存中查找数据,缓存中没有的话再去数据库中进行查询。

二级缓存(全局缓存)

第一章:一级缓存

一:证明一级缓存默认开启

证明事项:

1:一级缓存默认开启

2:作为一级缓存来讲,只对于本SqlSession有效,别的SqlSession用不了。

二:换SqlSession更普遍

日后的开发过程中,这种换SqlSession是不是常见的呢?换的场景是普遍的,这是为什么呢?

不同的请求过来服务端之后,会有多个请求线程到达我们的服务端,Controler-Service-Dao,

Controller和Service和Dao都有可能是单例或者是多例的。这样的SqlSession是不一样的。

为什么说两个SqlSession是不一样的呢?这是为什么呢?我们开发过程中SqlSession也好,Conneciton也好是不能共享的,一定是一个请求一份。这是因为在开发过程中,这两个对象他是会控制事务的。正因为他们是控制事务的,才不能共享。

那么为什么控制事务的就不能共享呢?这是为什么呢?会产生相互的影响。假如说SqlSession是可以共享的,第一个线程开始事务啥也没干,第二个线程直接把事务提交了,这样不就有问题了么,所以这玩意是不可以共享的。

查询不是不需要事务么?这是不对的,查询在特殊场景下是需要事务的。这个特殊场景是在加悲观锁的时候,必须要事务。第二种说的就是我们二级缓存必须加事务,这样我们的二级缓存才会有效果。

那一般场景查询不是不需要事务的么?实际场景应用的时候,我们极少有只进行select的时候,更多的时候是一个Service把增删改查全干了。所以,这玩意必须要换,一个请求一个。

Mybatis一级缓存的作用和目的并没有那么大了,我们提供缓存的目的是为了是Java程序第二次与查询相同数据的时候可以从缓存中获取,减少与数据库之间的交互。提高系统效率,但是一级缓存如此设计的话,切换了SqlSession是用不上的。一级缓存在Mybatis设计当中对于我们开发帮助是不大的,主要解决的是Mybatis的内部的一些问题,内部比如说关联关系当中解决回环引用的时候使用的,对于应用来讲作用不大。

三:一级缓存源码分析

1:支持回顾

Mybatis通过如下:

sqlSession.getMapper()生成了UserDao的实现类。(代理实现类)

在代理底层调用的时候,实际上调用的是sqlSession.selectAll(),selectOne等方法,在这些方法里边是基于excutor方法来执行的。一级缓存和二级缓存的接入点都是在Excutor当中接入的。

Excutor接口实现类关系如下:

知识补充:

这个结构是一个典型的适配器设计模式。

适配器设计模式解决的是什么问题呢?

适配器就是变压器,适配器就是层层实现即可。一层实现一部分。

什么时候使用适配器模式?

我们这个类只想实现某些方法,将接口中的抽象方法进行分层实现。

2:源码分析

以及缓存当中的核心源码体现在哪里呢?是在BaseExcutor当中。子类都能享受到这个一级缓存的功能。

BaseExcutor当中维护了缓存,这个缓存到底是一个HashMap,两个PerpetualCache缓存一个是基本操作的缓存,一个是存储过程的缓存。存储过程是数据库的编程语言。数据库的变成语言专业名词就叫做存储过程,存储过程逐渐被大数据解决方案代替。

protected PerpetualCache localCache;
  protected PerpetualCache localOutputParameterCache;

3:怎么接入缓存?

知识补充:

@Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    //拼接好的SQL
    BoundSql boundSql = ms.getBoundSql(parameter);
    //缓存Key
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    //执行真正的查询
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
 }
@Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    List<E> list;
    try {
      queryStack++;
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      //有缓存查缓存
      if (list != null) {
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      //没缓存查数据库
      } else {
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }
    if (queryStack == 0) {
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      // issue #601
      deferredLoads.clear();
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
        clearLocalCache();
      }
    }
    return list;
  }
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
      //查询数据库
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
      localCache.removeObject(key);
    }
    //查询数据库之后放到缓存当中。
    localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }

总结:

流程逻辑伴随着上述代码展开的,如果缓存中有数据直接返回,如果换出中没有数据为null,就查询数据库,查询数据库之后再将数据放到缓存当中便于下一次的查询。

第一次查询:

先做SQL-- 在做key–查询缓存–查询数据库–数据放到缓存中。

第二次查询:

先做SQL-- 在做key–查询缓存–直接返回数据。

相关文章
|
4月前
|
存储 缓存 NoSQL
mybatisplus一二级缓存
MyBatis-Plus 继承并优化了 MyBatis 的一级与二级缓存机制。一级缓存默认开启,作用于 SqlSession,适用于单次会话内的重复查询;二级缓存需手动开启,跨 SqlSession 共享,适合提升多用户并发性能。支持集成 Redis 等外部存储,增强缓存能力。
|
1月前
|
缓存 并行计算 监控
vLLM 性能优化实战:批处理、量化与缓存配置方案
本文深入解析vLLM高性能部署实践,揭秘如何通过continuous batching、PagedAttention与前缀缓存提升吞吐;详解批处理、量化、并发参数调优,助力实现高TPS与低延迟平衡,真正发挥vLLM生产级潜力。
428 0
vLLM 性能优化实战:批处理、量化与缓存配置方案
|
3月前
|
缓存 运维 安全
WordPress安全加速:Cloudflare + Nginx缓存优化方案
本文介绍如何通过Cloudflare与Nginx优化WordPress网站性能,涵盖静态资源长期缓存、动态页面智能缓存及敏感路径保护,提升加载速度并保障后台安全。适用于使用Cloudflare与Nginx环境的WordPress站点。
177 0
|
6月前
|
缓存 Java 数据库连接
Mybatis一级缓存详解
Mybatis一级缓存为开发者提供跨数据库操作的一致性保证,有效减轻数据库负担,提高系统性能。在使用过程中,需要结合实际业务场景选择性地启用一级缓存,以充分发挥其优势。同时,开发者需注意其局限性,并做好事务和并发控制,以确保系统的稳定性和数据的一致性。
233 20
|
7月前
|
SQL 存储 Java
Mybatis源码解析:详述初始化过程
以上就是MyBatis的初始化过程,这个过程主要包括SqlSessionFactory的创建、配置文件的解析和加载、映射文件的加载、SqlSession的创建、SQL的执行和SqlSession的关闭。这个过程涉及到了MyBatis的核心类和接口,包括SqlSessionFactory、SqlSessionFactoryBuilder、XMLConfigBuilder、XMLMapperBuilder、Configuration、SqlSession和Executor等。通过这个过程,我们可以看出MyBatis的灵活性和强大性,它可以很好地支持定制化SQL、存储过程以及高级映射,同时也避免了几
140 20
|
8月前
|
缓存 Java 数据库连接
Mybatis一级缓存、二级缓存详讲
本文介绍了MyBatis中的查询缓存机制,包括一级缓存和二级缓存。一级缓存基于同一个SqlSession对象,重复查询相同数据时可直接从缓存中获取,减少数据库访问。执行`commit`操作会清空SqlSession缓存。二级缓存作用于同一namespace下的Mapper对象,支持数据共享,需手动开启并实现序列化接口。二级缓存通过将数据存储到硬盘文件中实现持久化,为优化性能,通常在关闭Session时批量写入缓存。文章还说明了缓存的使用场景及注意事项。
302 7
Mybatis一级缓存、二级缓存详讲
|
10月前
|
Java 关系型数据库 数据库连接
简单易懂的 MyBatis 分库分表方案
本文介绍了一种基于 MyBatis 框架的数据库分库分表方案——shardino。不同于复杂插件方式,该方案通过客户端代码包装实现简便易懂的操作,显式处理分库分表逻辑,确保开发者清晰了解数据分布。项目地址:[https://github.com/pyloque/shardino](https://github.com/pyloque/shardino)。方案中,帖子表按 userId 字段 hash 分为 64 张表,平均分配到多个主从库中,配置文件管理 MySQL 组对象,支持读写分离和权重随机选择从库。代码示例展示了如何计算 partition number 并进行具体操作。
330 22
简单易懂的 MyBatis 分库分表方案
|
8月前
|
消息中间件 缓存 NoSQL
缓存与数据库的一致性方案,Redis与Mysql一致性方案,大厂P8的终极方案(图解+秒懂+史上最全)
缓存与数据库的一致性方案,Redis与Mysql一致性方案,大厂P8的终极方案(图解+秒懂+史上最全)
|
9月前
|
缓存 Java 数据库连接
十、MyBatis的缓存
十、MyBatis的缓存
196 6
|
10月前
|
缓存 NoSQL Java
Mybatis学习:Mybatis缓存配置
MyBatis缓存配置包括一级缓存(事务级)、二级缓存(应用级)和三级缓存(如Redis,跨JVM)。一级缓存自动启用,二级缓存需在`mybatis-config.xml`中开启并配置映射文件或注解。集成Redis缓存时,需添加依赖、配置Redis参数并在映射文件中指定缓存类型。适用于查询为主的场景,减少增删改操作,适合单表操作且表间关联较少的业务。
204 6