文档章节

Mybatis3.4.x技术内幕(二十二):Mybatis一级、二级缓存原理分析

祖大俊
 祖大俊
发布于 2016/09/17 13:06
字数 991
阅读 1814
收藏 7

Mybatis的一级缓存,指的是SqlSession级别的缓存,默认开启;Mybatis的二级缓存,指的是SqlSessionFactory级别的缓存,需要配置。缓存是针对select来说的。

1、一级缓存

<configuration>
	<settings>
		<setting name="localCacheScope" value="SESSION|STATEMENT" />
	</settings>
</configuration>

localCacheScope用于配置一级缓存的范围,默认值是SESSION,表示SqlSession范围;

如果配置为STATEMENT,则表示SqlSession范围内的一个查询范围,但它并不是一个Statement实例范围。

STATEMENT举例:查询Student对象发送一次sql查询,紧接着再发一次sql查询关联的Teacher对象,这个完整过程称之为一个查询。

一级缓存默认开启,且没有全局关闭的配置开关。

<select ... flushCache="false" useCache="true|false"/>

flushCache:同时影响了一级、二级缓存,flushCache=true,会导致清空本条sql(当前MappedStatement)的一级、二级缓存,注意是当前的,不影响其他的MappedStatement。

useCache:配置本条MappedStatement是否使用二级缓存,useCache=true,从二级缓存中获取,没有获取到,才从数据库中获取。

org.apache.ibatis.executor.CachingExecutor#query()方法源码:

Cache cache = ms.getCache();
    if (cache != null) {
      // flushCache作用于二级缓存
      flushCacheIfRequired(ms);
      // useCache作用于二级缓存
      if (ms.isUseCache() && resultHandler == null) {
        ensureNoOutParams(ms, parameterObject, boundSql);
        @SuppressWarnings("unchecked")
        List<E> list = (List<E>) tcm.getObject(cache, key);
        if (list == null) {
          list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

org.apache.ibatis.executor.BaseExecutor#query()方法源码:

if (queryStack == 0 && ms.isFlushCacheRequired()) {
      // flushCache作用于一级缓存
      clearLocalCache();
}

在执行update、insert、delete、flushCache="true"、commit、rollback、LocalCacheScope.STATEMENT等情况下,一级缓存就都会被清空。

缓存其实基本数据结构就是一个HashMap,缓存中是否存在缓存数据,依赖key的生成策略。

org.apache.ibatis.executor.BaseExecutor.createCacheKey()源码。

  @Override
  public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
    CacheKey cacheKey = new CacheKey();
    cacheKey.update(ms.getId());
    cacheKey.update(Integer.valueOf(rowBounds.getOffset()));
    cacheKey.update(Integer.valueOf(rowBounds.getLimit()));
    cacheKey.update(boundSql.getSql());
    for (int i = 0; i < parameterMappings.size(); i++) {
      //...
        cacheKey.update(value);
      }
    }
    if (configuration.getEnvironment() != null) {
      // issue #176
      cacheKey.update(configuration.getEnvironment().getId());
    }
    return cacheKey;
  }

id:com.mybatis3.mappers.TeacherMapper.findTeacherById

key的生成策略:id + offset + limit + sql + param value + environment id,这些值都相同,生成的key就相同。

2、二级缓存

<configuration>
	<settings>
		<setting name="cacheEnabled" value="true|false" />
	</settings>
</configuration>

cacheEnabled=true表示二级缓存可用,但是要开启话,需要在Mapper.xml内配置。

<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
if (cacheEnabled) {
      executor = new CachingExecutor(executor);
}

二级缓存通过CachingExecutor来实现,原理是缓存里存在,就返回,没有就调用Executor delegate到数据库中查询。

org.apache.ibatis.executor.CachingExecutor.query()源码。

  @Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, 
                           ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    Cache cache = ms.getCache();
    if (cache != null) {
      flushCacheIfRequired(ms);
      if (ms.isUseCache() && resultHandler == null) {
        ensureNoOutParams(ms, parameterObject, boundSql);
        @SuppressWarnings("unchecked")
        List<E> list = (List<E>) tcm.getObject(cache, key);
        if (list == null) {
          list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
    return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

FIFO:First In First Out先进先出队列。

flushInterval="60000",间隔60秒清空缓存,这个间隔60秒,是被动触发的,而不是定时器轮询的。

size=512,表示队列最大512个长度,大于则移除队列最前面的元素,这里的长度指的是CacheKey的个数。

CacheKey的生成策略,和一级缓存相同,id + offset + limit + sql + param value + environment id。

readOnly="true",表示任何获取对象的操作,都将返回同一实例对象。如果readOnly="false",则每次返回该对象的拷贝对象,简单说就是序列化复制一份返回。

二级缓存有一个非常重要的空间划分策略:

namespace="com.mybatis3.mappers.TeacherMapper"

namespace="com.mybatis3.mappers.StudentMapper"

即,按照namespace划分,同一个namespace,同一个Cache空间,不同的namespace,不同的Cache空间。

每当执行insert、update、delete,flushCache=true时,二级缓存都会被清空。

版权提示:文章出自开源中国社区,若对文章感兴趣,可关注我的开源中国社区博客(http://my.oschina.net/zudajun)。(经过网络爬虫或转载的文章,经常丢失流程图、时序图,格式错乱等,还是看原版的比较好)

© 著作权归作者所有

祖大俊
粉丝 797
博文 32
码字总数 52477
作品 0
昌平
私信 提问
加载中

评论(6)

xf_zhaojun
xf_zhaojun

引用来自“itxx2016”的评论

呵呵,写的很好啊.
推荐一个很不错的mybatis代码生成网站: fwjava.com
很流行的专门网站,很多知名的互联网企业都在用这个网站.
我也用过,绝对的好用.
下载了里面的demo看了一下,这生成的名字不符合规范,生成的东西作用不大
zmr51y
zmr51y
以前看过其他文章分析的,在不同的mapper中update同一个表时,二级缓存会出现脏数据,二级缓存的作用域时Mapper,所以不推荐用mybatis的二级缓存
祖大俊
祖大俊 博主

引用来自“rmqc0909”的评论

楼主,今天研究了一天的一级二级缓存,你这块说的:
一级缓存默认开启,且没有全局关闭的配置开关,只能单独指定一个sql查询是否使用缓存。
<select ... flushCache="false" useCache="true|false"/>
是不是不对呀,我看官方文档以及自己debug,useCache、flushCache这两个只跟二级缓存有关系吧。
针对select语句:
useCache  将其设置为 true,将会导致本条语句的结果被二级缓存,默认值:对 select 元素为 true。
flushCache  将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:false。
我重新调试了一级、二级缓存,你说的关于useCache和flushCache的描述是正确的,我也调整了我博客内容的描述,大致修改正确了,谢谢你这么细心。😄
祖大俊
祖大俊 博主

引用来自“rmqc0909”的评论

楼主,今天研究了一天的一级二级缓存,你这块说的:
一级缓存默认开启,且没有全局关闭的配置开关,只能单独指定一个sql查询是否使用缓存。
<select ... flushCache="false" useCache="true|false"/>
是不是不对呀,我看官方文档以及自己debug,useCache、flushCache这两个只跟二级缓存有关系吧。
针对select语句:
useCache  将其设置为 true,将会导致本条语句的结果被二级缓存,默认值:对 select 元素为 true。
flushCache  将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:false。
我下班回家调一下看看。😄
rmqc0909
rmqc0909
楼主,今天研究了一天的一级二级缓存,你这块说的:
一级缓存默认开启,且没有全局关闭的配置开关,只能单独指定一个sql查询是否使用缓存。
<select ... flushCache="false" useCache="true|false"/>
是不是不对呀,我看官方文档以及自己debug,useCache、flushCache这两个只跟二级缓存有关系吧。
针对select语句:
useCache  将其设置为 true,将会导致本条语句的结果被二级缓存,默认值:对 select 元素为 true。
flushCache  将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:false。
i
itxx2016
呵呵,写的很好啊.
推荐一个很不错的mybatis代码生成网站: fwjava.com
很流行的专门网站,很多知名的互联网企业都在用这个网站.
我也用过,绝对的好用.
通过源码分析MyBatis的缓存

前方高能! 本文内容有点多,通过实际测试例子+源码分析的方式解剖MyBatis缓存的概念,对这方面有兴趣的小伙伴请继续看下去~ MyBatis缓存介绍 首先看一段wiki上关于MyBatis缓存的介绍: MyBa...

whthomas
2014/12/11
20
0
mybatis学习笔记(14)-查询缓存之一级缓存

mybatis学习笔记(14)-查询缓存之一级缓存 标签: mybatis [TOC] 本文主要讲mybatis的一级缓存,一级缓存是SqlSession级别的缓存。 查询缓存 mybatis提供查询缓存,用于减轻数据压力,提高数据...

brianway
2016/03/02
105
0
MyBatis实战(三)-二级缓存原理解析

MyBatis的二级缓存是Application级别的缓存,它可以提高对数据库查询的效率,以提高应用的性能。本文将全面分析MyBatis的二级缓存的设计原理。 1.MyBatis的缓存机制整体设计以及二级缓存的工...

芥末无疆sss
2018/10/07
0
0
Mybatis 详解--- 一级缓存、二级缓存

Mybatis 为我们提供了一级缓存和二级缓存,可以通过下图来理解: ①、一级缓存是SqlSession级别的缓存。在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓...

Java干货分享
2018/09/12
88
0
Mybatis一级缓存、二级缓存

以下内容来自美团技术博客:聊聊MyBatis缓存机制 前言 MyBatis是常见的Java数据库访问层框架。在日常工作中,开发人员多数情况下是使用MyBatis的默认缓存配置,但是MyBatis缓存机制有一些不足...

为了美好的明天
2018/05/23
372
0

没有更多内容

加载失败,请刷新页面

加载更多

Springboot Redis详解

1. 在springboot中使用redis,只需要依赖spring-boot-starter-data-redis,然后在配置文件中配置spring.redis开头的一些配置,根据Redis的架构选择单节点,主从或集群模式,详情如下(2.0.0....

sunranhou
26分钟前
3
0
Android动画不执行

startAnimation 和 setAnimation 有一些区别 1、 imvIcon.startAnimation(anim);2、 imvIcon.setAnimation(anim); anim.startNow();...

安卓工程师王恒
38分钟前
2
0
一套基于SpringBoot+Vue+Shiro 前后端分离 开发的代码生成器

一、前言 最近花了一个月时间完成了一套基于Spring Boot+Vue+Shiro前后端分离的代码生成器,目前项目代码已基本完成 止步传统CRUD,进阶代码优化: 该项目可根据数据库字段动态生成 controll...

郑清
今天
8
0
javascript-十六进制随机颜色

<script> // 编写一个函数,获得一个十六进制的随机颜色的字符串(如#20CD4F) // function randomColor(){ // var r = random(0,255).toString(16); // var g = random(0,255).toString(16......

ACKo
今天
3
0
springBoot +mybatis 出现sql 语句在数据库可以查询到,但是赋值到实体类上就没有的情况?

1.不要老是反复查看自己是否写错了,为啥有的能出来有的出不来? 可以查看配置文件中是否配置全: 如果在application.yml 文件中是如下配置: mybatis: mapper-locations: classpath:mapp...

kuchawyz
今天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部