Mybatis源码(九)— chche

Mybatis中共有三级缓存,其中一级缓存默认开启,作用范围是在sqlSession对象(同一个会话),二级缓存需要手动配置开启,作用范围是在sqlSessionFactory对象下的同一个namespace范围(所以二级缓存是可以多个会话共享的,不过如果多表关联查询,会失效)。三级缓存不用太关注。

二级缓存配置

  1. mybatis-config.xml全局配置,不配置也可以,默认true。
    <setting name="cacheEnabled" value="true"/>
    
  2. 在对应的mapper.xml中对< cache>标签进行配置
    <!-- eviction:缓存的清除策略,当缓存达到上限后会自动触发对应算法清除缓存
    flushInterval:清除缓存的时间间隔,在指定时间会自动清理缓存,单位为毫秒
    size:缓存存储大小,指定保存对象的个数(List集合也算一个对象),一般不推荐将List集合放入缓存中,因为List集合较为多变,命中率较
    readOnly:是否为只读,设置为true代表数据缓存只读,每次从缓存取出的对象是对象本身,执行效率较高;设置为false代表读取的对象为缓存的副本,每次取出的对象是不同的,这种操作对数据较为安全 -->
    <cache eviction="LRU" flushInterval="60000" size="1000"/>
    <!-- 还可以在select、update等标签中,指定是否将结果放入缓存或执行后是否直接清除缓存-->
    <!-- 使用useCashe将结果集放入缓存中 -->
    <select id="xxx" resultType="xxx" useCache="true">
        SELECT * FROM xx 
    </select>
    <!-- 针对于某些情景下,想要在插入完成后就清空缓存可以使用flushCache属性,设置为true则会在SQL执行结束后立刻清空缓存 -->
    <update id="update" parameterType="com.mybatis.entity.Good" flushCache="true">
        UPDATE xx SET aa =  #{aa}
        WHERE bb = #{bb}
    </update>
    

Cache

Cache
cache包下的Cache接口约定了整个缓存的规范,有不同的Cache的实现类来实现它完成不同的功能。看类中方法可发现,其实就是相当于对一个map的存取。

package org.apache.ibatis.cache;

import java.util.concurrent.locks.ReadWriteLock;

/**
 * 缓存
 */
public interface Cache {

  /**
   * 该缓存对象的id
   */
  String getId();

  /**
   * 向缓存中添加数据,key是CacheKey,value是查询结果
   */
  void putObject(Object key, Object value);

  /**
   * 根据指定的key,在缓存中查找对应的结果对象
   *
   */
  Object getObject(Object key);

  /**
   * 删除key对应的缓存项
   *
   */
  Object removeObject(Object key);

  /**
   * 清空缓存
   */
  void clear();

  /**
   * 缓存项的个数
   *
   */
  int getSize();

  /**
   * 获取读写锁
   */
  default ReadWriteLock getReadWriteLock() {
    return null;
  }
}

在调用Configuration的无参构造时,同样会对常见的Cache对象的Alias(别名)做映射处理。

 public Configuration() {
    typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
    typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
    typeAliasRegistry.registerAlias("LRU", LruCache.class);
    typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
    typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
  }

接下来就根据Configuration映射的顺序,来逐一查看对应的缓存类。

PerpetualCache
PerpetualCache类代表永久缓存,用HashMap来实现对缓存的存储。

/**
 *
 * 永久缓存
 * 一旦存入就一直保持
 *
 * @author Clinton Begin
 */
public class PerpetualCache implements Cache {

  // Cache对象的唯一标识
  private final String id;

  // 记录缓存想的map对象
  private final Map<Object, Object> cache = new HashMap<>();

  public PerpetualCache(String id) {
    this.id = id;
  }

  @Override
  public String getId() {
    return id;
  }

  @Override
  public int getSize() {
    return cache.size();
  }

  @Override
  public void putObject(Object key, Object value) {
    cache.put(key, value);
  }

  @Override
  public Object getObject(Object key) {
    return cache.get(key);
  }

  @Override
  public Object removeObject(Object key) {
    return cache.remove(key);
  }

  @Override
  public void clear() {
    cache.clear();
  }

  @Override
  public boolean equals(Object o) {
    // 只要id相等就认为两个cache相同
    if (getId() == null) {
      throw new CacheException("Cache instances require an ID.");
    }
    if (this == o) {
      return true;
    }
    if (!(o instanceof Cache)) {
      return false;
    }

    Cache otherCache = (Cache) o;
    return getId().equals(otherCache.getId());
  }

  @Override
  public int hashCode() {
    if (getId() == null) {
      throw new CacheException("Cache instances require an ID.");
    }
    return getId().hashCode();
  }
}

除了上面的PerpetualCache,其余四个类都在org.apache.ibatis.cache.decorators包下,使用到了装饰者模式。

FifoCache
底层为了了一个LinkedList链表来记录了cache的先后顺序(每次从后添加,如果size > 1024,则从头先remove ),默认LinkedList大小为1024。除此之外,还是使用装饰者模式,调用其他的Cache对象来完成Cache的存储。

/**
 * FIFO缓存
 * 这个类就是维护一个FIFO链表,其他都委托给所包装的cache去做。典型的装饰模式
 *
 * FIFO (first in, first out) cache decorator.
 *
 */
public class FifoCache implements Cache {

  // 底层被装饰的底层Cache对象
  private final Cache delegate;
  // 用于记录key进入缓存的先后顺序
  private final Deque<Object> keyList;
  // 记录了缓存项的上限,超过该值,则需要清理最老的缓存项
  private int size;

  public FifoCache(Cache delegate) {
    this.delegate = delegate;
    this.keyList = new LinkedList<>();
    this.size = 1024;
  }

  @Override
  public String getId() {
    return delegate.getId();
  }

  @Override
  public int getSize() {
    return delegate.getSize();
  }

  public void setSize(int size) {
    this.size = size;
  }

  @Override
  public void putObject(Object key, Object value) {
    // 检测并清理缓存
    cycleKeyList(key);
    // 添加缓存项
    delegate.putObject(key, value);
  }

  @Override
  public Object getObject(Object key) {
    return delegate.getObject(key);
  }

  @Override
  public Object removeObject(Object key) {
    return delegate.removeObject(key);
  }

  @Override
  public void clear() {
    delegate.clear();
    keyList.clear();
  }

  private void cycleKeyList(Object key) {
    // 记录key
    keyList.addLast(key);
    // 如果缓存达到上限,则清理最老的缓存项
    if (keyList.size() > size) {
      Object oldestKey = keyList.removeFirst();
      delegate.removeObject(oldestKey);
    }
  }
}

LruCache
和FifoCache的区别在于,它使用了LinkedHashMap来记录着Cache的顺序,在进行过期策略时,删除最少使用的Cache。

/**
 * 最近最少使用缓存
 * 基于 LinkedHashMap 覆盖其 removeEldestEntry 方法实现。
 *
 * Lru (least recently used) cache decorator.
 *
 * @author Clinton Begin
 */
public class LruCache implements Cache {

  // 被装饰的底层cache对象
  private final Cache delegate;
  // 有序的hashmap,用于记录key最近的使用情况
  private Map<Object, Object> keyMap;
  // 记录最少被使用的缓存项的key
  private Object eldestKey;

  public LruCache(Cache delegate) {
    this.delegate = delegate;
    setSize(1024);
  }

  @Override
  public String getId() {
    return delegate.getId();
  }

  @Override
  public int getSize() {
    return delegate.getSize();
  }

  public void setSize(final int size) {
    // 重新设置缓存大小时,会重置keyMap字段,注意LinkedHashMap构造函数的第三个参数,true表示该LinkedHashMap记录的顺序是access-order
    keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {
      private static final long serialVersionUID = 4267176411845948333L;

      // 当调用LinkedHashMap.put方法,会调用此方法
      @Override
      protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
        boolean tooBig = size() > size;
        // 如果已达到缓存上线,则更新eldestKey字段,后面会删除该项
        if (tooBig) {
          // 把eldestKey存入实例变量
          eldestKey = eldest.getKey();
        }
        return tooBig;
      }
    };
  }

  @Override
  public void putObject(Object key, Object value) {
    // 添加缓存项
    delegate.putObject(key, value);
    // 删除最久未使用的缓存项
    cycleKeyList(key);
  }

  @Override
  public Object getObject(Object key) {
    // 修改LinkedHashMap中记录的顺序
    keyMap.get(key); // touch
    return delegate.getObject(key);
  }

  @Override
  public Object removeObject(Object key) {
    return delegate.removeObject(key);
  }

  @Override
  public void clear() {
    delegate.clear();
    keyMap.clear();
  }

  private void cycleKeyList(Object key) {
    keyMap.put(key, key);
    // eldestKey不为空,表示已经达到缓存上限
    if (eldestKey != null) {
      // 删除最久未使用的缓存项
      delegate.removeObject(eldestKey);
      eldestKey = null;
    }
  }
}

SoftCache
JVM中对象的引用关系分为强、软、弱、虚。每种引用关系在JVM进行垃圾回收时回收的标准是不同的,Mybatis的SoftCache其本质就是软引用。

public class SoftCache implements Cache {
  // 在SoftCache中,最近使用的一部分缓存项不会被GC回收,这就是通过将其value添加到hardLinksToAvoidGarbageCollection集合中实现的
  private final Deque<Object> hardLinksToAvoidGarbageCollection;
  // 引用队列,用于记录已经被GC回收的缓存项对应的SoftEntry对象
  private final ReferenceQueue<Object> queueOfGarbageCollectedEntries;
  // 底层被装饰的底层Cache对象
  private final Cache delegate;
  // 强连接的个数,默认是256
  private int numberOfHardLinks;

  public SoftCache(Cache delegate) {
    this.delegate = delegate;
    //默认链表可以存256元素
    this.numberOfHardLinks = 256;
    this.hardLinksToAvoidGarbageCollection = new LinkedList<>();
    this.queueOfGarbageCollectedEntries = new ReferenceQueue<>();
  }

  @Override
  public String getId() {
    return delegate.getId();
  }

  @Override
  public int getSize() {
    removeGarbageCollectedItems();
    return delegate.getSize();
  }

  public void setSize(int size) {
    this.numberOfHardLinks = size;
  }

  @Override
  public void putObject(Object key, Object value) {
    // 清除已经被GC回收的缓存项
    removeGarbageCollectedItems();
    // 向缓存中添加缓存项
    delegate.putObject(key, new SoftEntry(key, value, queueOfGarbageCollectedEntries));
  }

  @Override
  public Object getObject(Object key) {
    Object result = null;
    @SuppressWarnings("unchecked") // assumed delegate cache is totally managed by this cache
    // 从缓存中查找对应的缓存项
    SoftReference<Object> softReference = (SoftReference<Object>) delegate.getObject(key);
    // 检测缓存中是否有对应的缓存项
    if (softReference != null) {
      // 获取SoftReference引用的value
      result = softReference.get();
      // 已经被GC回收
      if (result == null) {
        // 从缓存中清除对应的缓存项
        delegate.removeObject(key);
        // 未被GC回收
      } else {
        // See #586 (and #335) modifications need more than a read lock
        synchronized (hardLinksToAvoidGarbageCollection) {
          // 缓存项的value添加到hardLinksToAvoidGarbageCollection集合中保存
          hardLinksToAvoidGarbageCollection.addFirst(result);
          if (hardLinksToAvoidGarbageCollection.size() > numberOfHardLinks) {
            // 超过numberOfHardLinks,将最老的缓存项从hardLinksToAvoidGarbageCollection集合中清除
            hardLinksToAvoidGarbageCollection.removeLast();
          }
        }
      }
    }
    return result;
  }

  @Override
  public Object removeObject(Object key) {
    removeGarbageCollectedItems();
    return delegate.removeObject(key);
  }

  @Override
  public void clear() {
    synchronized (hardLinksToAvoidGarbageCollection) {
      // 清理强引用集合
      hardLinksToAvoidGarbageCollection.clear();
    }
    // 清理被GC回收的缓存项
    removeGarbageCollectedItems();
    // 清除底层delegate缓存中的缓存项
    delegate.clear();
  }

  private void removeGarbageCollectedItems() {
    SoftEntry sv;
    // 遍历queueOfGarbageCollectedEntries集合
    while ((sv = (SoftEntry) queueOfGarbageCollectedEntries.poll()) != null) {
      // 将已经被GC回收的value对象对应的缓存项清除
      delegate.removeObject(sv.key);
    }
  }

  private static class SoftEntry extends SoftReference<Object> {
    private final Object key;

    SoftEntry(Object key, Object value, ReferenceQueue<Object> garbageCollectionQueue) {
      // 指向value的引用是软引用,且关联了引用队列
      super(value, garbageCollectionQueue);
      // 强引用
      this.key = key;
    }
  }
}

WeakCache
WeakCache同SoftCache

/**
 * 弱引用缓存,可以看到代码和SoftCache如出一辙,就是SoftReference变成了WeakReference
 *
 * Weak Reference cache decorator.
 * Thanks to Dr. Heinz Kabutz for his guidance here.
 *
 * @author Clinton Begin
 */
public class WeakCache implements Cache {
  private final Deque<Object> hardLinksToAvoidGarbageCollection;
  private final ReferenceQueue<Object> queueOfGarbageCollectedEntries;
  private final Cache delegate;
  private int numberOfHardLinks;

  public WeakCache(Cache delegate) {
    this.delegate = delegate;
    this.numberOfHardLinks = 256;
    this.hardLinksToAvoidGarbageCollection = new LinkedList<>();
    this.queueOfGarbageCollectedEntries = new ReferenceQueue<>();
  }

  @Override
  public String getId() {
    return delegate.getId();
  }

  @Override
  public int getSize() {
    removeGarbageCollectedItems();
    return delegate.getSize();
  }

  public void setSize(int size) {
    this.numberOfHardLinks = size;
  }

  @Override
  public void putObject(Object key, Object value) {
    removeGarbageCollectedItems();
    delegate.putObject(key, new WeakEntry(key, value, queueOfGarbageCollectedEntries));
  }

  @Override
  public Object getObject(Object key) {
    Object result = null;
    @SuppressWarnings("unchecked") // assumed delegate cache is totally managed by this cache
    WeakReference<Object> weakReference = (WeakReference<Object>) delegate.getObject(key);
    if (weakReference != null) {
      result = weakReference.get();
      if (result == null) {
        delegate.removeObject(key);
      } else {
        synchronized (hardLinksToAvoidGarbageCollection) {
          hardLinksToAvoidGarbageCollection.addFirst(result);
          if (hardLinksToAvoidGarbageCollection.size() > numberOfHardLinks) {
            hardLinksToAvoidGarbageCollection.removeLast();
          }
        }
      }
    }
    return result;
  }

  @Override
  public Object removeObject(Object key) {
    removeGarbageCollectedItems();
    return delegate.removeObject(key);
  }

  @Override
  public void clear() {
    synchronized (hardLinksToAvoidGarbageCollection) {
      hardLinksToAvoidGarbageCollection.clear();
    }
    removeGarbageCollectedItems();
    delegate.clear();
  }

  private void removeGarbageCollectedItems() {
    WeakEntry sv;
    while ((sv = (WeakEntry) queueOfGarbageCollectedEntries.poll()) != null) {
      delegate.removeObject(sv.key);
    }
  }

  private static class WeakEntry extends WeakReference<Object> {
    private final Object key;

    private WeakEntry(Object key, Object value, ReferenceQueue<Object> garbageCollectionQueue) {
      super(value, garbageCollectionQueue);
      this.key = key;
    }
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值