Mybaties的缓存机制:及缓存框架的使用

Mybaties中的缓存机制,是面试常出现的问题:包括一级缓存.二级缓存,集成二级缓存框架:(这里介绍redis):

一级缓存的剖析和源码:

1:Mybaties的一级缓存是默认开启的,作用范围是在同一个sqlsession中:sqlsession调用相同的查询方法时:会向缓存集合map中查询有没有缓存结果集:如果有直接取出:如果没有,查询数据库,并把查询结果封装到map中:key的值有6个部分组成:

一级缓存的xml更改方式:默认是开启的,不建议配置:

<setting name="localCacheScope" value="SESSION"/>

通过代码的方式来查看查询流程:图和代码:


public void testLocalCacheScope() throws Exception {

        SqlSession sqlSession1 = factory.openSession(true);

        SqlSession sqlSession2 = factory.openSession(true);

      StudentMapper studentMapper = sqlSession1.getMapper(StudentMapper.class);

      StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);

        System.out.println("studentMapper读取数据: " + studentMapper.getStudentById(1));

        System.out.println("studentMapper读取数据: " + studentMapper.getStudentById(1));

        System.out.println("studentMapper2更新了" + studentMapper2.updateStudentName("小岑",1) + "个学生的数据");

        System.out.println("studentMapper读取数据: " + studentMapper.getStudentById(1));

        System.out.println("studentMapper2读取数据: " + studentMapper2.getStudentById(1));

控制台查看:

第一次查询是从数据库查询的:第二次查询是从缓存中查询的:在进行了dml语句后事务会自动提交:这时发现控制台又从新从数据库来进行查询:


从缓存中查询数据:

一个SqlSession会持有一个Executor执行器,复杂查询缓存的维护

DefaultSqlSession持有BaseExecutor,BaseExecutor聚合了PerpetualCache:点开发现是个map集合:

执行器家族:负责缓存的查询维护:




注意:缓存类实现了Cache缓存类:

既然是个map:那key值是什么呢:Statement Id + Offset + Limmit + Sql + Params

在执行器中会创建key:


在执行query方法时:会先从缓存类中获取这个map的value:查看是不是为空:为空的话执行查询数据库方法:将value存到map中:


代码的最后:会进行配置文件的判断所以如果不需要缓存可以在mybatis的配置文件设置localCacheScope=statement

关于一级缓存清空的问题:在使用query.update方法时,会调用cleanCache()方法,来对缓存数据进行清除:


二级缓存:

二级缓存作用域:mapper.xml文件:也就是说只要是基于这个文件中的sql查询,都会进行缓存:

开启二级缓存:<setting name="cacheEnabled" value="true"/>config.xml文件中: mapper.xml文件中:配置:<cach>

配置属性

type:引入自定义的缓存类(需要写全类名)

eviction:清除策略,例如先进先出FIFO,最近最少使用LRU等

flushInterval:刷新间隔,以毫秒为单位

size:引用大小,最多可存储多少个结果集的引用(可以为任意正数)

readOnly:设置为只读,只读的缓存会给所有调用者返回缓存对象的相同实例,可读写的缓存会(通过序列化)返回缓存对象的拷贝,默认为false,速度慢但是更安全

blocking:是否使用阻塞缓存,默认为false,当指定为true时将采用BlockingCache进行封装,使用BlockingCache会在查询缓存时锁住对应的Key,如果缓存命中了则会释放对应的锁,否则会在查询数据库以后再释放锁这样可以阻止并发情况下多个线程同时查询数据

二级缓存是Application级的缓存,一级缓存缓存的是SQL语句,而二级缓存缓存的是结果对象,二级缓存会存储一级缓存的内容

数据会先存入一级缓存中,二级缓存会从一级缓存中获取数据:

当开启了二级缓存后,我们再查询的时候,这时候的Executor实现类就是CachingExecutor了。源码实现

@Override

  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)

      throws SQLException {

//从MappedStatement中获取二级缓存

    Cache cache = ms.getCache();

    if (cache != null) {

      flushCacheIfRequired(ms);

      if (ms.isUseCache() && resultHandler == null) {

        ensureNoOutParams(ms, boundSql);

        @SuppressWarnings("unchecked")

        //从缓存中获取数据

        List<E> list = (List<E>) tcm.getObject(cache, key);

        if (list == null) {

          //如果获取不到,则会调用BaseExecutor的query方法

          list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

          //放入TransactionalCacheManager中,实际上是放入TransactionalCache这个对象的一个map容器中,然后在sqlsession提交或者close的时候更新二级缓存

          tcm.putObject(cache, key, list); // issue #578 and #116

        }

        return list;

      }

    }

    return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

二级缓存的查询逻辑大概流程就是先从二级缓存中获取,如果获取不到则调用BaseExecutor的query方法,如果开启了一级缓存,就再从一级缓存查询,否则从数据库查询数据;放入一级缓存中:最后将缓存数据存入TransactionalCache对象的map容器中,等待sqlsession提交或者close再更新到二级缓存:也就是说二级缓存必须要提交事务,或是关闭sqlsession后才会生效:原因如下:

//把缓存put到TransactionalCache的map容器中 getTransactionalCache(cache).putObject(key, value);

public void commit() {

    if (clearOnCommit) {

      delegate.clear();

    }

    flushPendingEntries();

    reset();

  }

  private void flushPendingEntries() {

    for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {

    //添加到缓存对象中,最终缓存对象也是使用个map来存储

      delegate.putObject(entry.getKey(), entry.getValue());

    }

    for (Object entry : entriesMissedInCache) {

      if (!entriesToAddOnCommit.containsKey(entry)) {

        delegate.putObject(entry, null);

      }

二级缓存的主要构建过程就是在初始化时先获取mapper文件中cache标签,并得到标签中设置的属性,然后通过建造者模式构建缓存对象,在具体的构建过程中又是通过装饰者模式实现根据不同的属性组合,优雅的为二级缓存添加相应属性功能。

二级缓存失效策略

1、二级缓存失效同一级缓存一样,增删改都会造成缓存清除

2、默认情况不是同一个namespace也不会走同一个缓存

3、自身属性配置的刷新时间和淘汰策略。



Mybatis集成redis

redis常用类

  1.1 Jedis

      jedis就是集成了redis的一些命令操作,封装了redis的java客户端

  1.2 JedisPoolConfig

      Redis连接池

  1.3 ShardedJedis

      基于一致性哈希算法实现的分布式Redis集群客户端

  实现 mybatis 的二级缓存,一般来说有如下两种方式:

  1) 采用 mybatis 内置的 cache 机制。

  2) 采用三方 cache 框架, 比如ehcache, oscache 等等.

在集群环境下使用mybaties的二级缓存:

这里只讲一下配置方式和原理:

首先作为二级缓存框架:需要事项cach类:mybaties自定义了redisCach的接口:

同样是需要在配置文件中开启缓存:

<settingname="cacheEnabled"value="true"/>

edis会自动的将Sql+条件+Hash等当做key值,而将查询结果作为value,只有请求中的所有参数都符合,那么就会使用redis中的二级缓存。其查询结果如下:

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,287评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,346评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,277评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,132评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,147评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,106评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,019评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,862评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,301评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,521评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,682评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,405评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,996评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,651评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,803评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,674评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,563评论 2 352

推荐阅读更多精彩内容