MyBatis:二級緩存原理分析

MyBatis從入門到放棄七:二級緩存原理分析

前言

        提及mybatis的一級緩存和二級緩存我特地問了幾個身邊的朋友他們平時會不會用,結果沒有一我的平時業務場景中用。 好吧,那我暫且用來學習源碼吧。一級緩存我我的認爲也確實有些雞肋,mybatis默認開啓一級緩存,支持在同一個會話(sqlsession)同一個statement執行兩次,則第二次會默認會使用第一次建立的緩存對象。html

         二級緩存前一篇粗略介紹了下,默認使用內存對象【PerpetualCache】內部維護一個HashMap對象來存儲。java

 

MyBatis緩存設計及二級緩存工做模式

        從上面三張圖中咱們得出結論,一級緩存是sqlsession級別、二級緩存是Mapper級別。mybatis定義了【Cache】接口,支持自定義緩存,同時還支持集成第三方緩存庫,如今爲了更細粒度的控制緩存,更多的集成【ehcache】、【redis】。redis

        那麼mybatis的二級緩存主要是在Executor對象上來作文章,當mybatis發現你在mybatis.xml配置文件中設置了cacheEnabled=true時,mybatis在建立sqlsession時建立Executor對象,同時會對Executor加上裝飾者【CacheExecutor】。CacheExecutor對於查詢請求,會判斷application級別的二級緩存是否有緩存結果,若是有查詢結果則直接返回,若是沒有再交給查詢器Executor實現類,也就是【SimpleExecutor】來執行查詢。再就是緩存結果,返回給用戶。算法

       貼出SmpleExecutor源碼:sql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
/**
 *    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。express

複製代碼
<?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.xmlapache

      這裏配置Cache對象。這裏的eviction參數提到幾個算法策略:緩存

     LRU:(Least Recently Used),最近最少使用算法,即若是緩存中容量已經滿了,會將緩存中最近作少被使用的緩存記錄清除掉,而後添加新的記錄;session

     FIFO:(First in first out),先進先出算法,若是緩存中的容量已經滿了,那麼會將最早進入緩存中的數據清除掉;mybatis

     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

   第一次查詢是36條數據,我配置緩存是2分鐘。而後再insert一條,你再刷新頁面數據不變。等兩分鐘。。。。

 

總結

       OK,所謂的存在即合理吧,適合不適合取決於你的業務場景。mybatis也提供了接口以便擴展。小流量、數據量小使用mybatis二級緩存足以。

相關文章
相關標籤/搜索