Mybatis3.4.x技術內幕(二十二):Mybatis一級、二級緩存原理分析

Mybatis的一級緩存,指的是SqlSession級別的緩存,默認開啓;Mybatis的二級緩存,指的是SqlSessionFactory級別的緩存,須要配置。緩存是針對select來講的。java

一、一級緩存

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

localCacheScope用於配置一級緩存的範圍,默認值是SESSION,表示SqlSession範圍;sql

若是配置爲STATEMENT,則表示SqlSession範圍內的一個查詢範圍,但它並非一個Statement實例範圍。數據庫

STATEMENT舉例:查詢Student對象發送一次sql查詢,緊接着再發一次sql查詢關聯的Teacher對象,這個完整過程稱之爲一個查詢。apache

一級緩存默認開啓,且沒有全局關閉的配置開關。緩存

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

flushCache:同時影響了一級、二級緩存,flushCache=true,會致使清空本條sql(當前MappedStatement)的一級、二級緩存,注意是當前的,不影響其餘的MappedStatement。網絡

useCache:配置本條MappedStatement是否使用二級緩存,useCache=true,從二級緩存中獲取,沒有獲取到,才從數據庫中獲取。數據結構

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

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()方法源碼:app

if (queryStack == 0 && ms.isFlushCacheRequired()) {
      // flushCache做用於一級緩存
      clearLocalCache();
}

在執行update、insert、delete、flushCache="true"、commit、rollback、LocalCacheScope.STATEMENT等狀況下,一級緩存就都會被清空。ide

緩存其實基本數據結構就是一個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就相同。

二、二級緩存

<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)。(通過網絡爬蟲或轉載的文章,常常丟失流程圖、時序圖,格式錯亂等,仍是看原版的比較好)

相關文章
相關標籤/搜索