MyBatisMyBatis从入门到丢弃七:二级缓存原理分析

前言

       
说起mybatis的一流缓存和二级缓存我特别问了多少个身边的恋人他们平常会不会用,结果尚未一个人平日事务场景中用。
好吧,这自己暂且用来上学源码吧。顶级缓存我个人觉得也确实有些鸡肋,mybatis默认开启顶尖缓存,帮助在同一个对话(sqlsession)同一个statement执行一次,则第二次会默认会使用首次创建的缓存对象。

       
 二级缓存前一篇粗略介绍了下,默认使用内存对象【PerpetualCache】内部维护一个HashMap对象来存储。那么先来看几张图纸【图片来源于一位情人,作品末了参考连接我会挂出作者博客】

 

MyBatis缓存设计及二级缓存工作格局

MyBatis 1

MyBatis 2

MyBatis 3

       
从地点三张图中大家得出结论,一流缓存是sqlsession级别、二级缓存是Mapper级别。mybatis定义了【Cache】接口,扶助自定义缓存,同时还匡助集成第三方缓存库,现在为了更细粒度的控制缓存,更多的集成【ehcache】、【redis】。

       
那么mybatis的二级缓存紧假设在Executor对象上来做小说,当mybatis发现你在mybatis.xml配置文件中安装了cacheEnabled=true时,mybatis在创立sqlsession时创设Executor对象,同时会对Executor加上装饰者【CacheExecutor】。CacheExecutor对于查询请求,会咬定application级其它二级缓存是否有缓存结果,假若有查询结果则直接回到,如果没有再交由查询器Executor实现类,也就是【SimpleExecutor】来施行查询。再不怕缓存结果,再次来到给用户。

       贴出SmpleExecutor源码:

/**
 *    Copyright 2009-2016 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.executor;

import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.transaction.Transaction;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
import java.util.List;

/**
 * @author Clinton Begin
 */
public class SimpleExecutor extends BaseExecutor {

  public SimpleExecutor(Configuration configuration, Transaction transaction) {
    super(configuration, transaction);
  }

  @Override
  public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.update(stmt);
    } finally {
      closeStatement(stmt);
    }
  }

  @Override
  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);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

  @Override
  protected <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException {
    Configuration configuration = ms.getConfiguration();
    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, null, boundSql);
    Statement stmt = prepareStatement(handler, ms.getStatementLog());
    return handler.<E>queryCursor(stmt);
  }

  @Override
  public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
    return Collections.emptyList();
  }

  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
  }

}

  那么,缓存起来的多少怎么过期呢,这也是自己这篇著作重点关注的。一般流量不大的站点,数据由后台维护,使用二级缓存丰硕了。先来看全局配置文件,配置只关心一点:cacheEnabled=true。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
        <!--开启二级缓存-->
        <setting name="cacheEnabled" value="true"/>
    </settings>
    <typeAliases>
        <typeAlias alias="User" type="com.autohome.model.User" />
    </typeAliases>

    <mappers>
        <mapper resource="mapper/UserMapper.xml" />
    </mappers>

</configuration>

      Mapper.xml

      这里配置Cache对象。这里的eviction参数提到多少个算法策略:

     LRU:(Least Recently
Used)
,近期起码使用算法,即只要缓存中容量已经满了,会将缓存中近日做少被使用的缓存记录清除掉,然后添加新的笔录;

     FIFO:(First in first
out)
,先进先出算法,如果缓存中的容量已经满了,那么会将起初进入缓存中的数据清除掉;

     Scheduled:指定时间间隔清空算法,该算法会以指定的某一个刻钟距离将Cache缓存中的数据清空;

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--命名空间和接口保持一致-->
<mapper namespace="com.autohome.dao.UserMapper">

    <!--
    eviction LRU
    flushInterval缓存时间,以毫秒为单位
    size缓存大小
    readOnly如果为false的话,缓存对象必须是可序列化的-->
    <cache eviction="LRU"
           type="org.apache.ibatis.cache.impl.PerpetualCache"
           flushInterval="120000"
           size="1024"
           readOnly="true"/>

    <select id="listAllUser" resultType="User">
        select * from t_userinfo
    </select>


</mapper>

 

配图看demo

MyBatis 4

 
 第一次查询是36条数据,我安排缓存是2分钟。然后再insert一条,你再刷新页面数据不变。等两秒钟。。。。

MyBatis 5

 

总结

     
 OK,所谓的留存即合理吗,适合不符合取决于你的政工场景。mybatis也提供了接口以便扩展。小流量、数据量小应用mybatis二级缓存足以。

 

参考

      http://blog.csdn.net/luanlouis/article/details/41408341

 

网站地图xml地图