美文网首页
(3)一级缓存和二级缓冲

(3)一级缓存和二级缓冲

作者: Mrsunup | 来源:发表于2018-10-29 00:11 被阅读0次

先可以参考下面的文章,写得比较详细: https://www.cnblogs.com/happyflyingpig/p/7739749.html

1.一级缓存的总结

image.png

如上图,当一个sqlSession第二次查询id为1的用户,本地缓存就被触发了,如果中间发现了修改,插入,删除并且提交了,一级缓存会被清空,这也展示了如何来验证以及缓存的情况

mybatis的一级缓存是基于sqlSession来缓存的。默认的情况下开启的,好处就是减少对数据库的压力

  • 补充一点:一级缓存对应的源码实现

对应源码:org.apache.ibatis.executor.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 (this.closed) {
            throw new ExecutorException("Executor was closed.");
        } else {
            if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
                this.clearLocalCache();
            }

            List list;
            try {
                ++this.queryStack;  
            //尝试从缓存中获取对象
                list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
                if (list != null) {
                  //处理参数绑定
                    this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
                } else {
                    list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
                }
            } finally {
                --this.queryStack;
            }

            if (this.queryStack == 0) {
                Iterator i$ = this.deferredLoads.iterator();

                while(i$.hasNext()) {
                    BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)i$.next();
                    deferredLoad.load();
                }

                this.deferredLoads.clear();
                if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
                    this.clearLocalCache();
                }
            }

            return list;
        }
    }
  • CacheKey怎么生成

对应着mybatis的源码org.apache.ibatis.executor.BaseExecutor#createCacheKey对一个cashKey的生成方式如下:

 public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
        if (this.closed) {
            throw new ExecutorException("Executor was closed.");
        } else {
            CacheKey cacheKey = new CacheKey();
            //传入的statementId
            cacheKey.update(ms.getId());
            //对应着sql从哪条记录开始查
            cacheKey.update(rowBounds.getOffset());
            //对应着sql查几条
            cacheKey.update(rowBounds.getLimit());
          //对应着sql语句
            cacheKey.update(boundSql.getSql());
        //得到参数映射信息
            List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
            TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
            Iterator i$ = parameterMappings.iterator();

            while(i$.hasNext()) {
                ParameterMapping parameterMapping = (ParameterMapping)i$.next();
            //参数模式为out类型,这种情况在执行存储过程和方法的时候存在
                if (parameterMapping.getMode() != ParameterMode.OUT) {
                    String propertyName = parameterMapping.getProperty();
                    Object value;
                    if (boundSql.hasAdditionalParameter(propertyName)) {
                        value = boundSql.getAdditionalParameter(propertyName);
                    } else if (parameterObject == null) {
                        value = null;
                    } else if 
  //是否有字段的映射处理器
(typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                        value = parameterObject;
                    } else {
                        MetaObject metaObject = this.configuration.newMetaObject(parameterObject);
                        value = metaObject.getValue(propertyName);
                    }
                //参数的信息
                    cacheKey.update(value);
                }
            }

            if (this.configuration.getEnvironment() != null) {
              //配置环境的id
                cacheKey.update(this.configuration.getEnvironment().getId());
            }

            return cacheKey;
        }
    }

总结: CashKey的维度

  • 1.传入的statementId
  • 2.结果集中的结果范围
  • 3.sql语句
  • 4.传递的参数信息
  • 5.配置开发环境id

2.二级缓存的总结

总结:
MyBatis的二级缓存是mapper级别的缓存,它可以提高对数据库查询的效率,以提高应用的性能。mybatis的二级缓存默认是关闭,一般不建议使用, 可以使用redis或者第三方开启的缓存框架来使用。

二级缓存的问题:

  • 缓存是以namespace为单位的,不同namespace下的操作互不影响。
    insert,update,delete操作会清空所在namespace下的全部缓存。
  • 还可能造成脏数据的产生

模拟脏数据的产生


image.png

BlogMapper的有一个查询语句,其中blog中包含了Author的对象,此时AuthorMapper的有一个方法更新了Author的信息,此时blogMapper的对象中blog关联的author并不会更新,当二级缓存就会命中,那么此时的author的信息就不是最新的,脏数据就会产生

可以通过修改mybatis的配置文件来开启缓存

  <setting name="cacheEnabled" value="true" />

如果某个mapper文件需要使用,需要配置上如下的配置,LRU表示最近最少使用的,一处最长时间不用的对象

    <cache eviction="LRU" flushInterval="100000" readOnly="true" size="1024"/>

在statement中设置useCache=false可以禁用当前select语句的二级缓存,即每次查询都会发出sql去查询,默认情况是true,即该sql使用二级缓存。

 <!--可以通过设置useCache来规定这个sql是否开启缓存,ture是开启,false是关闭,默认情况是true-->
    <select id="selectAllStudents" resultMap="studentMap" useCache="true">
        SELECT id, name, age FROM student
    </select>

相关文章

网友评论

      本文标题:(3)一级缓存和二级缓冲

      本文链接:https://www.haomeiwen.com/subject/ymxvtqtx.html