手把手带你阅读Mybatis源码(三)缓存篇(上)

简介: 手把手带你阅读Mybatis源码(三)缓存篇(上)

前言


大家好,这一篇文章是MyBatis系列的最后一篇文章,前面两篇文章:手把手带你阅读Mybatis源码(一)构造篇手把手带你阅读Mybatis源码(二)执行篇,主要说明了MyBatis是如何将我们的xml配置文件构建为其内部的Configuration对象和

MappedStatement对象的,然后在第二篇我们说了构建完成后MyBatis是如何一步一步地执行我们的SQL语句并且对结果集进行封装的。


那么这篇作为MyBatis系列的最后一篇,自然是要来聊聊MyBatis中的一个不可忽视的功能,一级缓存和二级缓存


何谓缓存?


虽然这篇说的是MyBatis的缓存,但是我希望正在学习计算机的小伙伴即使还没有使用过MyBatis框架也能看明白今天这篇文章。


缓存是什么?我来说说个人的理解,最后再上比较官方的概念。


缓存(Cache),顾名思义,有临时存储的意思。计算机中的缓存,我们可以直接理解为,存储在内存中的数据的容器,这与物理存储是有差别的,由于内存的读写速度比物理存储高出几个数量级,所以程序直接从内存中取数据和从物理硬盘中取数据的效率是不同的,所以有一些经常需要读取的数据,设计师们通常会将其放在缓存中,以便于程序对其进行读取。


但是,缓存是有代价的,刚才我们说过,缓存就是在内存中的数据的容器,一条64G的内存条,通常可以买3-4块1T-2T的机械硬盘了,所以缓存不能无节制地使用,这样成本会剧增,所以一般缓存中的数据都是需要频繁查询,但是又不常修改的数据


而在一般业务中,查询通常会经过如下步骤。


读操作 --> 查询缓存中已经存在数据 -->如果不存在则查询数据库,如果存在则直接查询缓存-->数据库查询返回数据的同时,写入缓存中。

写操作 --> 清空缓存数据 -->写入数据库


image.png


比较官方的概念


☞ 缓存就是数据交换的缓冲区(称作:Cache),当某一硬件要读取数据时,会首先从缓存汇总查询数据,有则直接执行,不存在时从内存中获取。由于缓存的数据比内存快的多,所以缓存的作用就是帮助硬件更快的运行。


☞ 缓存往往使用的是RAM(断电既掉的非永久存储),所以在用完后还是会把文件送到硬盘等存储器中永久存储。电脑中最大缓存就是内存条,硬盘上也有16M或者32M的缓存。


☞ 高速缓存是用来协调CPU与主存之间存取速度的差异而设置的。一般CPU工作速度高,但内存的工作速度相对较低,为了解决这个问题,通常使用高速缓存,高速缓存的存取速度介于CPU与主存之间。系统将一些CPU在最近几个时间段经常访问的内容存在高速缓存,这样就在一定程度上缓解了由于主存速度低造成的CPU“停工待料”的情况。


☞ 缓存就是把一些外存上的数据保存在内存上而已,为什么保存在内存上,我们运行的所有程序里面的变量都是存放在内存中的,所以如果想将值放入内存上,可以通过变量的方式存储。在JAVA中一些缓存一般都是通过Map集合来实现的。


MyBatis的缓存


在说MyBatis的缓存之前,先了解一下Java中的缓存一般都是怎么实现的,我们通常会使用Java中的Map,来实现缓存,所以在之后的缓存这个概念,就可以把它直接理解为一个Map,存的就是键值对。


一级缓存简介


MyBatis中的一级缓存,是默认开启且无法关闭的一级缓存默认的作用域是一个SqlSession,解释一下,就是当SqlSession被构建了之后,缓存就存在了,只要这个SqlSession不关闭,这个缓存就会一直存在,换言之,只要SqlSession不关闭,那么这个SqlSession处理的同一条SQL就不会被调用两次,只有当会话结束了之后,这个缓存才会一并被释放。


虽说我们不能关闭一级缓存,但是作用域是可以修改的,比如可以修改为某个Mapper。


一级缓存的生命周期:


1、如果SqlSession调用了close()方法,会释放掉一级缓存PerpetualCache对象,一级缓存将不可用。


2、如果SqlSession调用了clearCache(),会清空PerpetualCache对象中的数据,但是该对象仍可使用。


3、SqlSession中执行了任何一个update操作(update()、delete()、insert()) ,都会清空PerpetualCache对象的数据,但是该对象可以继续使用。


节选自:https://www.cnblogs.com/happyflyingpig/p/7739749.html


image.png


二级缓存简介


MyBatis的二级缓存是默认关闭的,如果要开启有两种方式:


1.在mybatis-config.xml中加入如下配置片段


<!-- 全局配置参数,需要时再设置 -->
     <settings>
            <!-- 开启二级缓存  默认值为true -->
         <setting name="cacheEnabled" value="true"/>
     </settings>


2.在mapper.xml中开启


<!--开启本mapper的namespace下的二级缓存-->
     <!--
             eviction:代表的是缓存回收策略,目前MyBatis提供以下策略。
             (1) LRU,最近最少使用的,一处最长时间不用的对象
             (2) FIFO,先进先出,按对象进入缓存的顺序来移除他们
             (3) SOFT,软引用,移除基于垃圾回收器状态和软引用规则的对象
             (4) WEAK,弱引用,更积极的移除基于垃圾收集器状态和弱引用规则的对象。
                 这里采用的是LRU,  移除最长时间不用的对形象
             flushInterval:刷新间隔时间,单位为毫秒,如果你不配置它,那么当
             SQL被执行的时候才会去刷新缓存。
             size:引用数目,一个正整数,代表缓存最多可以存储多少个对象,不宜设置过大。设置过大会导致内存溢出。
             这里配置的是1024个对象
             readOnly:只读,意味着缓存数据只能读取而不能修改,这样设置的好处是我们可以快速读取缓存,缺点是我们没有
             办法修改缓存,他的默认值是false,不允许我们修改
      -->
     <cache eviction="回收策略" type="缓存类"/>


二级缓存的作用域与一级缓存不同,一级缓存的作用域是一个SqlSession,但是二级缓存

的作用域是一个namespace,什么意思呢,你可以把它理解为一个mapper,在这个mapper中操作的所有SqlSession都可以共享这个二级缓存。但是假设有两条相同的SQL,写在不同的namespace下,那这个SQL就会被执行两次,并且产生两份value相同的缓存。


MyBatis缓存的执行流程


依旧是用前两篇的测试用例,我们从源码的角度看看缓存是如何执行的。


public static void main(String[] args) throws Exception {
    String resource = "mybatis.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    //从调用者角度来讲 与数据库打交道的对象 SqlSession
    DemoMapper mapper = sqlSession.getMapper(DemoMapper.class);
    Map<String,Object> map = new HashMap<>();
    map.put("id","2121");
    //执行这个方法实际上会走到invoke
    System.out.println(mapper.selectAll(map));
    sqlSession.close();
    sqlSession.commit();
  }


这里会执行到query()方法:


public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    //二级缓存的Cache,通过MappedStatement获取
    Cache cache = ms.getCache();
    if (cache != null) {
      //是否需要刷新缓存
      //在<select>标签中也可以配置flushCache属性来设置是否查询前要刷新缓存,默认增删改刷新缓存查询不刷新
      flushCacheIfRequired(ms);
      //判断这个mapper是否开启了二级缓存
      if (ms.isUseCache() && resultHandler == null) {
        //不管
        ensureNoOutParams(ms, boundSql);
        @SuppressWarnings("unchecked")
        //先从缓存拿
        List<E> list = (List<E>) tcm.getObject(cache, key);
        if (list == null) {
            //如果缓存等于空,那么查询一级缓存
          list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          //查询完毕后将数据放入二级缓存
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        //返回
        return list;
      }
    }
    //如果二级缓存为null,那么直接查询一级缓存
    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}


可以看到首先MyBatis在查询数据时会先看看这个mapper是否开启了二级缓存,如果开启了,会先查询二级缓存,如果缓存中存在我们需要的数据,那么直接就从缓存返回数据,如果不存在,则继续往下走查询逻辑。


接着往下走,如果二级缓存不存在,那么就直接查询数据了吗?答案是否定的,二级缓存如果不存在,MyBatis会再查询一次一级缓存,接着往下看。


相关文章
|
2月前
|
缓存 Java 数据库连接
mybatis复习05,mybatis的缓存机制(一级缓存和二级缓存及第三方缓存)
文章介绍了MyBatis的缓存机制,包括一级缓存和二级缓存的配置和使用,以及如何整合第三方缓存EHCache。详细解释了一级缓存的生命周期、二级缓存的开启条件和配置属性,以及如何通过ehcache.xml配置文件和logback.xml日志配置文件来实现EHCache的整合。
mybatis复习05,mybatis的缓存机制(一级缓存和二级缓存及第三方缓存)
|
2月前
|
SQL XML Java
mybatis-源码深入分析(一)
mybatis-源码深入分析(一)
|
20天前
|
SQL 缓存 Java
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
本文详细介绍了MyBatis的各种常见用法MyBatis多级缓存、逆向工程、分页插件 包括获取参数值和结果的各种情况、自定义映射resultMap、动态SQL
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
|
20天前
|
SQL 缓存 Java
MyBatis如何关闭一级缓存(分注解和xml两种方式)
MyBatis如何关闭一级缓存(分注解和xml两种方式)
61 5
|
1月前
|
缓存 Java 数据库连接
使用MyBatis缓存的简单案例
MyBatis 是一种流行的持久层框架,支持自定义 SQL 执行、映射及复杂查询。本文介绍了如何在 Spring Boot 项目中集成 MyBatis 并实现一级和二级缓存,以提高查询性能,减少数据库访问。通过具体的电商系统案例,详细讲解了项目搭建、缓存配置、实体类创建、Mapper 编写、Service 层实现及缓存测试等步骤。
|
1月前
|
缓存 NoSQL Ubuntu
大数据-39 Redis 高并发分布式缓存 Ubuntu源码编译安装 云服务器 启动并测试 redis-server redis-cli
大数据-39 Redis 高并发分布式缓存 Ubuntu源码编译安装 云服务器 启动并测试 redis-server redis-cli
56 3
|
1月前
|
前端开发 Java 数据库连接
表白墙/留言墙 —— 中级SpringBoot项目,MyBatis技术栈MySQL数据库开发,练手项目前后端开发(带完整源码) 全方位全步骤手把手教学
本文是一份全面的表白墙/留言墙项目教程,使用SpringBoot + MyBatis技术栈和MySQL数据库开发,涵盖了项目前后端开发、数据库配置、代码实现和运行的详细步骤。
51 0
表白墙/留言墙 —— 中级SpringBoot项目,MyBatis技术栈MySQL数据库开发,练手项目前后端开发(带完整源码) 全方位全步骤手把手教学
|
1月前
|
Java 数据库连接 mybatis
Springboot整合Mybatis,MybatisPlus源码分析,自动装配实现包扫描源码
该文档详细介绍了如何在Springboot Web项目中整合Mybatis,包括添加依赖、使用`@MapperScan`注解配置包扫描路径等步骤。若未使用`@MapperScan`,系统会自动扫描加了`@Mapper`注解的接口;若使用了`@MapperScan`,则按指定路径扫描。文档还深入分析了相关源码,解释了不同情况下的扫描逻辑与优先级,帮助理解Mybatis在Springboot项目中的自动配置机制。
136 0
Springboot整合Mybatis,MybatisPlus源码分析,自动装配实现包扫描源码
|
1月前
|
消息中间件 缓存 NoSQL
Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。
【10月更文挑战第4天】Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。随着数据增长,有时需要将 Redis 数据导出以进行分析、备份或迁移。本文详细介绍几种导出方法:1)使用 Redis 命令与重定向;2)利用 Redis 的 RDB 和 AOF 持久化功能;3)借助第三方工具如 `redis-dump`。每种方法均附有示例代码,帮助你轻松完成数据导出任务。无论数据量大小,总有一款适合你。
77 6
|
14天前
|
缓存 NoSQL 关系型数据库
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
本文详解缓存雪崩、缓存穿透、缓存并发及缓存预热等问题,提供高可用解决方案,帮助你在大厂面试和实际工作中应对这些常见并发场景。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
下一篇
无影云桌面