mybatis的一级二级缓存详解及源码解剖

这篇具有很好参考价值的文章主要介绍了mybatis的一级二级缓存详解及源码解剖。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


什么是一级缓存?

一级缓存是指在同一个SqlSession中,对于相同的查询语句和参数,第一次查询的结果会被缓存到内存中,后续的查询会直接从缓存中获取结果,而不会再次查询数据库。一级缓存是MyBatis默认开启的,可以通过在SqlSession中调用clearCache()方法来清空缓存。


什么是二级缓存?

二级缓存是指在多个SqlSession中,对于相同的查询语句和参数,第一次查询的结果会被缓存到内存中,后续的查询会直接从缓存中获取结果,而不会再次查询数据库。二级缓存是需要手动开启的,可以通过在Mapper.xml文件中添加标签来开启。二级缓存的作用范围是Mapper级别的,也就是说,同一个Mapper.xml文件中的查询语句会共享同一个缓存。


一级缓存二级缓存有什么不同?

mybatis的一级二级缓存详解及源码解剖

一级缓存和二级缓存的不同点在于作用范围和生命周期。一级缓存的作用范围是SqlSession级别的,生命周期也是和SqlSession一样的,当SqlSession关闭时,缓存也会被清空。而二级缓存的作用范围是Mapper级别的,生命周期是和应用程序一样的,当应用程序关闭时,缓存也会被清空。另外,二级缓存需要手动开启,而一级缓存是默认开启的。


执行流程

mybatis的一级二级缓存详解及源码解剖

  public void testMybatis()throws Exception{
		
    SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
    org.springframework.core.io.ClassPathResource classPathResource=new ClassPathResource("org/apache/ibatis/user/mybatis.xml");
    InputStream inputStream = classPathResource.getInputStream();
    // 1.读取配置文件获取SqlSessionFactory 
    SqlSessionFactory sqlSessionFactory= sqlSessionFactoryBuilder.build(inputStream);
    // 2.获取sqlsession
    SqlSession sqlSession = sqlSessionFactory.openSession();
	
	// 3.执行获取结果
    User user = new User(1L, null, null, null);
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> users = mapper.selectUser(user);
    mapper.selectUser(user);
    System.out.println(users);
    }

CachingExecutor 二级缓存执行器

CachingExecutor类直接实现了Excutor接口,是装饰器类,主要用来增强缓存相关功能。在CachingExecutor类中,为了完成缓存相关功能,需要TransactionalCacheManager和TransactionalCache两个类的支持,其中TransactionalCacheManager类主要用来管理缓存数据的对象TransactionalCache,而TransactionalCache对象是用来真正缓存数据的对象。

BaseExecutor 一级缓存执行器

BaseExecutor是MyBatis中所有Executor的基类,它实现了Executor接口中的一些通用方法,例如query()、update()、flushStatements()等。BaseExecutor中定义了一个MappedStatement和CacheKey的Map,用于缓存已经解析过的MappedStatement和CacheKey对象,以提高查询效率。BaseExecutor还定义了一个Transaction对象,用于管理事务的提交和回滚。

SimpleExecutor(默认是SimpleExecutor)每次执行SQL语句都需要进行编译

SimpleExecutor是最简单的Executor实现,每次执行SQL语句时都会创建一个新的Statement对象,执行完毕后立即关闭Statement对象。SimpleExecutor适用于短时间内需要执行大量SQL语句的场景,但是由于每次都需要创建和关闭Statement对象,所以效率相对较低。

ReuseExecutor 执行相同SQL语句时,如果之前SQL已编译,则不会在进行编译。

ReuseExecutor是在SimpleExecutor的基础上进行了优化,它会在执行SQL语句之前先检查是否已经存在可重用的Statement对象,如果存在则直接使用,否则创建一个新的Statement对象。ReuseExecutor适用于需要执行多个相同的SQL语句的场景,可以减少创建和关闭Statement对象的次数,提高效率。

BatchExecutor 批量执行

BatchExecutor是用于批量执行SQL语句的Executor实现,它会将多个SQL语句合并成一个批量操作,然后一次性发送给数据库执行。BatchExecutor适用于需要执行大量相似的SQL语句的场景,可以减少网络传输和数据库操作的次数,提高效率。

在MyBatis中,可以通过在配置文件中设置defaultExecutorType属性来指定默认的Executor实现,也可以在Mapper.xml文件中通过executorType属性来指定特定的Executor实现。


源码流程解剖

一:获取UserMapper

我们先来看看 UserMapper mapper = sqlSession.getMapper(UserMapper.class);这一行代码返回的是一个什么对象。
mybatis的一级二级缓存详解及源码解剖

1.首先我们可以看到,mybatis通过动态代理模式给我们生成了一个Usermapper对象。

2.在这个mapper对象当中,有一个sqlSession对象,SqlSession里面有一个executor,这个executor是CachingExecutor(这个是二级缓存的CachingExecutor)

3.在CachingExecutor中有一个delegate它是一个SimpleExecutor。(这里就要注意了,因为BaseExecutor是SimpleExecutor的父类,只不过mybatis默认是SimpleExecutor。所以这里delegate其实是指向了一级缓存BaseExecutor)

二:执行UserMapper.selectUser(user)的查询方法

由于我们的UserMapper是动态代理生成的代理对象,那么在执行方法前会先去执行invoke方法

List<User> users = mapper.selectUser(user);

MapperProxy代理对象

  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      }
      // 这个就是调用mapperMethod.execute方法,看当前查询是select、update、delete等等
      // 在根据具体的实现去调用DefaultSqlSession里面的(selectList()、insert()等等)
      return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }

DefaultSqlSession,因为我们查询的是一个数组,所以就调用了selectList()方法

  private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
    try {
     // 1. 获取一个statement
      MappedStatement ms = configuration.getMappedStatement(statement);
      dirty |= ms.isDirtySelect();
      // 2.执行二级缓存CachingExecutor的query方法
      return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

CachingExecutor 二级缓存的query方法

  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler)
      throws SQLException {
    // 这就是解析你的SQL,把SQL解析为 select * from user wehre id=?
    // 并且包含了SQL语句和参数列表等信息
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    // CacheKey对象中包含了MappedStatement的ID、查询参数、分页参数等信息,这个比较重要。后面会详细讲解
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

CachingExecutor 二级开始干事情的query方法

  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler,
      CacheKey key, BoundSql boundSql) throws SQLException {
     // 1.获取当前mappernamespace是否配置了<cache/>标签,是否开启了二级缓存 ,从二级缓存空气获取该cache
    Cache cache = ms.getCache();
    if (cache != null) {
    	// 判断是否要清除缓存,只会清空当前线程的缓存,不会影响其他线程的缓存。
      flushCacheIfRequired(ms);
      if (ms.isUseCache() && resultHandler == null) {
       // 检查SQL语句中是否包含输出参数
        ensureNoOutParams(ms, boundSql);
        // 从二级缓存中获取结果
        List<E> list = (List<E>) tcm.getObject(cache, key);
        
        // 从一级缓存中获取结果并将结果存储到二级缓存map当中
        if (list == null) {
          list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        //  从二级缓存中拿出结果,进行返回
        return list;
      }
    }
    // delegate是我们的一级缓存,如果没有开启二级缓存,就直接调用一级缓存获取结果进行返回
    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

BaseExecutor的query方法

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) {
      // 处理本地缓存中的输出参数,不会对SQL语句进行解析或执行
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
      	// 一级缓存没有就去数据库查询结构,后面会调用SimpleExecutor的doQuery方法。 
      	// 而且会把查询数据存入一级缓存当中
        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;
  }

SimpleExecutor的doQuery

public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler,
      BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler,
          boundSql);
       // 获取连接 ,会调用PooledDataSource.getConnection()
      stmt = prepareStatement(handler, ms.getStatementLog());
      // 查询数据库
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

StatementHandler.query最后会调用PreparedStatementHandler的query方法

// 看到这里大家就非常熟悉了,和jdbc一样PreparedStatement 执行SQL语句进行返回。
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.handleResultSets(ps);
  }

一级缓存失效场景分析

在上面我们从一级缓存获取数据时,都是通过key来获取的。我们来看看这个key到底是啥。

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) {
      // 处理本地缓存中的输出参数,不会对SQL语句进行解析或执行
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
    
  }

从一级缓存获取数据
list = resultHandler == null ? (List) localCache.getObject(key) : null;

这个CacheKey key,前面我也标注了是在二级缓存调用query方法时获取的。那我们来看看这个key里面有啥玩意
mybatis的一级二级缓存详解及源码解剖
在updateList中有6参数我们来依次看看啥意思。
参数 0:指向的就是我们statementID
参数1:分页数值
参数2:分页数值
参数3:编译后的SQL语句
参数4:设置的参数值
参数5:就是你配置数据源时的default值

<environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="com.mysql.jdbc.Driver"/>
        <property name="url" value=""/>
        <property name="username" value=""/>
        <property name="password" value=""/>
      </dataSource>
    </environment>
  </environments>

那么我们就可以总结一级缓存有哪些失效场景了。(修改,新增操作就忽略)

  1. 必须在同一个statementID的语句中执行
  2. SQL语句和参数值必须一致。
  3. 分页条件必须一致
  4. 必须在同一个会话中,在上面代码中我们也可以看到。我们调用mapper.selectUser(user);是会执行sqlSession的excutor方法。如果sqlSession关闭了,那么里面的一级执行器,当然也就不会在存在了。

这里可能有疑问,sqlSession什么时候销毁呢?

戳这里,在spring中sqlSession的创建及销毁


二级缓存结构及需要解决的问题

首先我们需要考虑的是,既然二级缓存是可以在不同的sqlSession进行数据共享,那么会有哪些问题需要我们去解决。

问题一:线程安全问题,既然二级缓存可以跨线程使用。那么我们是不是要考虑线程安全、脏读等问题。

问题二:既然是缓存,不是存储在内存中,就是存储在磁盘或者等三方中间件redis中。所以它们肯定是有一定的空间大小的,那就需要一些缓存淘汰策略。

问题三:在mybatis中,二级缓存是存在一个命中率记录器的。那么每次查询命中二级缓存,也是需要进行记录的。那么在并发情况下,怎么保证二级缓存命中率的准确性

等等,当然还有一些其他需要考虑问题,我就不一 一列举,

如果让我们设计一个获取缓存的方法,需要解决以上问题的话。那还不简单,直接撸一个方法,把这些解决方法逻辑加上去,就完事。但是在mybatis中,并没有采用这种方式,而是采用了责任链和装饰器设计模式。
mybatis的一级二级缓存详解及源码解剖

在mybatis当中,首先定义了一个Cache接口,Cache接口定义了一些获取缓存,存放缓存等等一些方法。

通过责任链设计模式,让每一个处理逻辑的Cache去实现Cache接口,在每一个方法中做自己做的事。这样做可以达到我们的解耦和单一职责。

通过装饰器设计模式,当我们处理完线程同步的逻辑后,就会执行下一个cache对应的方法,最后一直执行到我们的内存存储的cache。这样做可以达到解耦,对原有方法进行增强,减少我们的继承类。

总之mybatis这样做的好处,就在于对代码进行可维护可拓展。如果后续有需求需要新加一个cache的逻辑,我们就可以不改动原代码,只需去编写新逻辑的cahche在把它加进去即可。

下面我们来验证下,是不是这样。

 Cache cache = sqlSession.getConfiguration().getCache("org.apache.ibatis.mapper.UserMapper");
    cache.putObject("123",users);

大家可以发现第一个cache是SynchronizedCache,第二个cache是LoggingCache,第三个cache是LRUCache,第四个cache是PerptualCache。其实就和我们上面的流程图一致
mybatis的一级二级缓存详解及源码解剖


二级缓存执行流程

mybatis的一级二级缓存详解及源码解剖

在之前流程图中,我做了一些改动。多了一个事务缓存管理器和一个缓存空间。

事务缓存管理器是存储我们当前sqlSession中二级缓存所存储的数据。当SqlSession关闭时,那么这个事务缓存管理器也会失效。为什么要有这个事务缓存管理器呢,因为我们的二级缓存是需要在提交后,在进行存储的。如果进行实时提交,事务后面出现回滚等操作。就会出现脏读等问题。


二级缓存获取和commit源码解剖

Debug解析

在没有执行SQL语句之前,TransactionalCacheManager中的transactionalCaches是空,没有任何数据。这是因为我们还没有执行SQL语句,mybatis也不知道需要将那些数据加入到我们的TransactionalCacheManager当中,你查询了哪个缓存mapper,就会将哪个缓存的mapper加入到我们的TransactionalCacheManager当中。
mybatis的一级二级缓存详解及源码解剖
在我们执行一次查询后,看看有什么变化
mybatis的一级二级缓存详解及源码解剖
当我们commit之后,会有什么变化
mybatis的一级二级缓存详解及源码解剖


命中二级缓存源码解剖

CachingExecutor 的query方法

  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler,
      CacheKey key, BoundSql boundSql) throws SQLException {
     // 1.获取当前mappernamespace是否配置了<cache/>标签,是否开启了二级缓存 ,从二级缓存空间中获取对应的cache
    Cache cache = ms.getCache();
    if (cache != null) {
    	// 判断是否要清除缓存,只会清空当前线程的缓存,不会影响其他线程的缓存。
      flushCacheIfRequired(ms);
      if (ms.isUseCache() && resultHandler == null) {
       // 检查SQL语句中是否包含输出参数
        ensureNoOutParams(ms, boundSql);
        // 从二级缓存中获取结果
        List<E> list = (List<E>) tcm.getObject(cache, key);
        
        // 从一级缓存中获取结果并将结果存储到二级缓存map当中
        if (list == null) {
          list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        //  从二级缓存中拿出结果,进行返回
        return list;
      }
    }
    // delegate是我们的一级缓存,如果没有开启二级缓存,就直接调用一级缓存获取结果进行返回
    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

我们进入tcm也就是TransactionalCacheManager的getobject方法

  public Object getObject(Cache cache, CacheKey key) {
    return getTransactionalCache(cache).getObject(key);
  }
  
// 获取一个TransactionalCache对象,并将其存储在缓存中,以便在事务中使用。如果缓存中已经存在这个对象,则直接返回;否则,创建一个新的对象并存储在缓存中。
  private TransactionalCache getTransactionalCache(Cache cache) {
    return MapUtil.computeIfAbsent(transactionalCaches, cache, TransactionalCache::new);
  }
 public Object getObject(Object key) {
    // 从二级缓存空间获取数据
    Object object = delegate.getObject(key);
   // 如果没有获取到
    if (object == null) {
    // 将当前数据加到事务缓存管理器中,进行暂时存储
      entriesMissedInCache.add(key);
    }
    // 判断该值是否为true。如果是true就返回null。
    // 因为在我们事务没提交时,如果进行修改的时候不会直接清除二级缓存空间,而是先标记一下做个假删除。等commit之后才会去清除我们的二级缓存空间。
    // 同样标记为null,也是为了防止出现脏读等。
    if (clearOnCommit) {
      return null;
    }
    return object;
  }

mybatis的一级二级缓存详解及源码解剖


commit源码解剖

DefaultSqlSession的commit

  public void commit(boolean force) {
    try {
    // 调用二级缓存的commit
      executor.commit(isCommitOrRollbackRequired(force));
      dirty = false;
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error committing transaction.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

CachingExecutor的commit
mybatis的一级二级缓存详解及源码解剖
TransactionalCacheManager的commit

  public void commit() {
  // 循环进行提交
    for (TransactionalCache txCache : transactionalCaches.values()) {
    // 调用TransactionalCache的commit
      txCache.commit();
    }
  }

TransactionalCache的commit

  public void commit() {
    // 如果标记假删除,这里提交时候就会清空
    if (clearOnCommit) {
      delegate.clear();
    }
    // 进行刷新
    flushPendingEntries();
    reset();
  }

// 看到这里是不是就是很熟悉了,将我们事务缓存管理器的map丢到我们的二级缓存空间当中。
  private void flushPendingEntries() {
    for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
      delegate.putObject(entry.getKey(), entry.getValue());
    }
    for (Object entry : entriesMissedInCache) {
      if (!entriesToAddOnCommit.containsKey(entry)) {
        delegate.putObject(entry, null);
      }
    }
  }

至于二级缓存失效,第一个二级缓存只有提交后才会生效,第二个发生增删改就会清空所有的二级缓存。


总结

如上就是对myabtis的一级缓存二级缓存进行分析和源码解剖,如下就做个总结。

1.mybatis一级缓存和二级缓存的区别

1.作用范围不同:一级缓存是SqlSession级别的缓存,只在当前SqlSession中有效;而二级缓存是Mapper级别的缓存,多个SqlSession可以共享同一个Mapper的二级缓存。

2.存储位置不同:一级缓存是存储在SqlSession内部的一个HashMap中,而二级缓存是存储在外部的缓存中,例如Ehcache、Redis等。

3.失效机制不同:一级缓存的失效机制是基于SqlSession的生命周期,当SqlSession关闭时,一级缓存也会被清空;而二级缓存的失效机制是基于时间的,可以通过配置缓存的过期时间来控制缓存的失效。

4.数据一致性不同:一级缓存可以保证数据的一致性,因为它只在当前SqlSession中有效;而二级缓存可能会出现数据不一致的问题,因为多个SqlSession可以共享同一个Mapper的二级缓存。

5.配置方式不同:一级缓存是默认开启的,无需进行额外的配置;而二级缓存需要进行额外的配置,包括缓存的类型、大小、过期时间等。

总之,MyBatis的一级缓存和二级缓存都是用于提高查询性能的缓存机制,但是它们的作用范围、存储位置、失效机制、数据一致性和配置方式都有所不同。在使用MyBatis时,需要根据具体的业务需求和系统性能要求,选择合适的缓存机制。


2.一级缓存失效原因有哪些

  1. 必须在同一个statementID的语句中执行
  2. SQL语句和参数值必须一致。
  3. 分页条件必须一致
  4. 必须在同一个会话中,在上面代码中我们也可以看到。我们调用mapper.selectUser(user);是会执行sqlSession的excutor方法。如果sqlSession关闭了,那么里面的一级执行器,当然也就不会在存在了。

3.为什么不建议使用mybatis的二级缓存?

虽然MyBatis的二级缓存可以提高查询性能,但是在实际应用中,使用二级缓存并不总是一个好的选择。以下是一些原因:

1.数据不一致性问题:二级缓存是一个全局的缓存,多个SqlSession共享同一个缓存。如果在一个SqlSession中修改了数据,但是这个修改没有及时更新缓存,那么在另一个SqlSession中查询这个数据时,就会出现数据不一致的问题。

2.内存占用问题:二级缓存是一个全局的缓存,会占用大量的内存。如果缓存中存储了大量的数据,那么就会导致内存占用过高,从而影响系统的性能。

3.缓存失效问题:二级缓存的失效机制是基于时间的,如果缓存中的数据长时间没有被访问,那么就会被自动清除。但是这种失效机制并不总是可靠的,有时候缓存中的数据可能已经过期,但是仍然被使用,从而导致数据不一致的问题。

4.配置复杂性问题:使用二级缓存需要进行复杂的配置,包括缓存的类型、大小、过期时间等。如果配置不当,就会导致缓存的效果不佳,甚至影响系统的性能。

5.综上所述,虽然MyBatis的二级缓存可以提高查询性能,但是在实际应用中,使用二级缓存需要考虑到数据一致性、内存占用、缓存失效和配置复杂性等问题。因此,建议在使用MyBatis时,根据具体的业务需求和系统性能要求,谨慎选择是否使用二级缓存。文章来源地址https://www.toymoban.com/news/detail-451727.html

到了这里,关于mybatis的一级二级缓存详解及源码解剖的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Mybatis 中的一级缓存与二级缓存

      缓存的意义是将用户经常查询的数据放入缓存(内存)中去,用户去查询数据的时候就不需要从磁盘(关系型数据库)中查询,直接从缓存中查询,从而提高了查询效率,解决了高并发中系统的性能问题。Mybatis中提供一级缓存与二级缓存。   Mybatis的一级缓存是一个

    2024年02月08日
    浏览(43)
  • Mybatis一级缓存和二级缓存(带测试方法)

    目录 相关导读 一、什么是缓存  二、Mabtis一级缓存 (1)测试一级缓存

    2023年04月08日
    浏览(79)
  • Mybatis的一级、二级缓存怎样使用?

    一级缓存基于PerpetualCache的HashMap本地缓存,其存储作用域为Session,当Session进行flush或close之后,该Session中的所有Cache就将清空,默认打开一级缓存。 二级缓存是基于namespace和mappe的作用域起作用的,不是依赖于SQL session,默认也是采用PerpetualCache,HashMap存储 当某一个作用域

    2024年02月16日
    浏览(43)
  • mybatis分页、延迟加载、立即加载、一级缓存、二级缓存

    分类 : 使用Limit,来进行分页;物理分页 使用RowBounds集合来保存分页需要数据,来进行分页;逻辑分页;本质是全查,只是显示部分 使用分页插件来进行分页;物理分页 方式一: 方式二: 方式三: 首先导入两个jar包: 配置插件: 调用: 字段 含义 pageNum 当前页的页码 pa

    2024年01月18日
    浏览(56)
  • Mr. Cappuccino的第55杯咖啡——Mybatis一级缓存&二级缓存

    缓存越小,查询速度越快,缓存数据越少 缓存越大,查询速度越慢,缓存数据越多 在多级缓存中,一般常见的是先查询一级缓存,再查询二级缓存,但在Mybatis中是先查询二级缓存,再查询一级缓存。 在Mybatis中,BaseExecutor属于一级缓存执行器,CachingExecutor属于二级缓存执行

    2024年02月14日
    浏览(44)
  • MyBatis-Plus一级缓存和二级缓存-redis解决缓存的脏数据

    什么是缓存? 1.存在内存中的临时数据 2.将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库 数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。 为什么使用缓存 减少和数据库的交互次数,减少系统开

    2024年02月09日
    浏览(41)
  • CaffeineCache+Redis 接入系统做二层缓存思路实现(借鉴 mybatis 二级缓存、自动装配源码)

    现在手上有个系统写操作比较少,很多接口都是读操作,也就是写多读少,性能上遇到瓶颈了,正所谓前人栽树、后人乘凉,原先系统每次都是查数据库的,性能比较低,如果先查 redis,redis 没数据再查数据库的话,但是还可以更快,那就是使用内存查询,依次按照内存、

    2024年02月09日
    浏览(44)
  • MyBatis缓存-提高检索效率的利器--一级缓存

    😀前言 本篇博文是关于MyBatis一级缓存的介绍使用和缓存失效情况分析,希望能够帮助到您😊 🏠个人主页:晨犀主页 🧑个人简介:大家好,我是晨犀,希望我的文章可以帮助到大家,您的满意是我的动力😉😉 💕欢迎大家:这里是CSDN,我总结知识的地方,欢迎来到我的博

    2024年02月14日
    浏览(46)
  • 手敲Mybatis(16章)-一级缓存功能实现

    这一节的目的主要是实现SqlSession级别的缓存,也就是一级缓存,首先看下图一,用户可以通过设置来进行是否开启一级缓存,不设置的化默认开启一级缓存,localCacheScope=SESSION为要设置一级缓存,localCacheScope=STATEMENT为不要设置一级缓存,(所以后面在清理缓存时会进行判断,

    2024年02月01日
    浏览(42)
  • MyBatis缓存和二级缓存整合Redis

    缓存验证 在⼀个sqlSession中,对user表根据username进⾏两次查询,查看他们发出sql语句的情况 查看控制台打印情况: 看控制台输出可以看出来,第一次执行了SQL查询,第二次直接打印的结果集,没有查询数据库。 同样是对user表进⾏两次查询,只不过两次查询之间进⾏了⼀次

    2024年02月06日
    浏览(69)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包