Mybatis缓存的设计,通过Cache接口实现类:
- PerpetualCache:永久缓存,只有这唯一一个基础实现(org.apache.ibatis.cache.impl),其他实现类全都是装饰模式(org.apache.ibatis.cache.decorators)持有另一个缓存对象;
- PerpetualCache里面就是维护着一个本地的HashMap,所以不支持多线程,因为没能实现接口的锁功能,而且内部的map不是并发的map;
- FifoCache(先进先出缓存策略的缓存装饰类):通过双向队列Deque维护一个缓存key的列表,在添加缓存的时候检查队列大小,超过队列设定最大值则采用先进先出移除缓存key和缓存委托对象中的存储。
- LruCache(最近最少使用的缓存策略的缓存装饰类):通过LinkedHashMap双链回环循环链表的构造LRU算法链表,利用元素插入后总是会执行removeEldestEntry检查链表存储大小,来删除最近最少使用的缓存项key。
- SoftCache(软引用回收策略缓存装饰类):通过LinkedList链表实现对缓存value数据的强引用和ReferenceQueue队列实现对缓存value的软饮用,添加缓存委托对象数据时使用对缓存数据value的软饮用,当获取缓存项的数据不为null时,将软应用的缓存数据同时放到强引用的LinkedLik链表中变成强引用,当强引用链表大小大于numberOfHardLinks存储大小时,按照先进先出删除强引用链表移除先进入的缓存数据。然后在指定操作时机通过removeGarbageCollectedItems函数,移除被垃圾回收的键,和vlaue。软引用只有当内存不足时才会被垃圾收集器回收。
- WeakCache(弱引用回收策略缓存装饰类):同SoftCache,通过LinkedList链表实现对缓存value数据的强引用和WeakReference队列实现对缓存value的弱饮用,弱引用的对象一旦被垃圾收集器发现,则会被回收,无论内存是否足够。
- LoggingCache(日志功能装饰类,用于记录缓存的命中率):通过内部记录缓存请求次数和缓存命中次数,在debug模式下面输出命中率日志。
- ScheduledCache(定时清空Cache类):通过记录定时清空时间间隔和上次清空时间,来定时清空Cache,但是并没有开始一个定时任务,而是在使用Cache的时候,才去检查计算时间是否到了。
- SerializedCache(序列化功能,将值序列化后存到缓存中):序列化缓存数据功能,将存储数据序列化后存到缓存中,缓存返回时进行反序列化返回一份实例的Copy,保存线程安全。
- SynchronizedCache(同步的缓存Cache类):实现比较简单,直接使用synchronized修饰缓存操作的各种方法。
- TransactionalCache(二级缓存的事务缓存类):装饰了二级缓存对象;事务中需要进行二级缓存的各类数据保存在HashMap中,在事务提交后才会真正刷新到二级缓存中;或事务回退后被丢弃,对缓存没有影响。
FifoCache(先进先出缓存策略的缓存装饰类):
// FifoCache 源码
package org.apache.ibatis.cache.decorators;
public class FifoCache implements Cache {
// 缓存委托对象,真正缓存存储的对象
private final Cache delegate;
// 维护先进先出策略的队列,用来存储缓存项key的队列,key一般为CacheKey
// Deque是Queue的子接口,且是双向队列,它支持从两个端点方向检索和插入元素
// 因此Deque既可以支持FIFO形式也可以支持LIFO形式
private final Deque<Object> keyList;
// 最大缓存树,队列的大小,初始化时为1024个
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) {
// 缓存项key列表周期检查函数
cycleKeyList(key);
// 缓存委托对象添加缓存数据
delegate.putObject(key, value);
}
// 获取指定缓存项key的缓存,通过缓存委托对象
@Override
public Object getObject(Object key) {
return delegate.getObject(key);
}
// 删除指定缓存项key的缓存,通过缓存委托对象
@Override
public Object removeObject(Object key) {
return delegate.removeObject(key);
}
// 清空缓存
@Override
public void clear() {
// 清空缓存/队列
delegate.clear();
keyList.clear();
}
// 缓存项key列表周期检查函数
private void cycleKeyList(Object key) {
keyList.addLast(key);
// 队列大小超过缓存最大数,则队首元素出列,然后删除缓存委托对象中的key相关缓存
if (keyList.size() > size) {
Object oldestKey = keyList.removeFirst();
delegate.removeObject(oldestKey);
}
}
}
LruCache(最近最少使用的缓存策略的缓存装饰类):
// LruCache 源码
package org.apache.ibatis.cache.decorators;
public class LruCache implements Cache {
// 缓存委托对象,真正缓存存储的对象
private final Cache delegate;
// Map用来记录每个key使用情况的存储Map
private Map<Object, Object> keyMap;
// 记录keyMap中最近最少使用的元素的key
private Object eldestKey;
public LruCache(Cache delegate) {
this.delegate = delegate;
// 初始化LRU链表
setSize(1024);
}
@Override
public String getId() {
return delegate.getId();
}
@Override
public int getSize() {
return delegate.getSize();
}
// 每次调用该方法,都会重新新建一个LinkeHashMap实例对赋值给keyMap
public void setSize(final int size) {
// initialCapacity -> size 初始存储大小
// loadFactor -> .75F 默认负载因子(0.75),当容量不够时用于扩容计算,按照capacity*loadFactor取整后增长
// accessOrder -> LRU顺序
// true: 按访问顺序构造LRU内部链表,通过get方法调用排序,最近最少访问的排在最前面, 最多访问的排在后面. 迭代整个值是按链表从后往前的方式
// false: 默认值,表示按照插入顺序存储,按照调用put方法插入的顺序进行排序的
keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {
private static final long serialVersionUID = 4267176411845948333L;
@Override
// 钩子方法,由业务自己实现,Map在元素插入后总是会执行,返回结果为true则删除eldest,默认返回false
// accessOrder为true时,返回true则删除最不经常使用的元素
protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
// 链表的大小大于设定的存储容量
boolean tooBig = size() > size;
if (tooBig) {
// 记录最近最少使用的元素的key
eldestKey = eldest.getKey();
}
// 如果为true,则删除记录最近最少使用的元素eldest
return tooBig;
}
};
}
@Override
public void putObject(Object key, Object value) {
// 缓冲委托对象添加缓存
delegate.putObject(key, value);
// 周期性检查LRU链表
cycleKeyList(key);
}
@Override
// 获取缓存数据
public Object getObject(Object key) {
// 调用get方法,访问次数增加
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) {
// 向LRU的链表中添加元素
keyMap.put(key, key);
// 如果最近最少使用的元素key不为null,则删除缓存委托对象对应key的缓存数据
if (eldestKey != null) {
delegate.removeObject(eldestKey);
// 置空
eldestKey = null;
}
}
}
SoftCache(软引用回收策略缓存装饰类):
// SoftCache 源码
package org.apache.ibatis.cache.decorators;
// 软引用只有当内存不足时才会被垃圾收集器回收。这里的实现机制中,使用了一个链表来保证一定数量的值即使内存不足也不会被回收,但是没有保存在该链表的值则有可能会被回收
public class SoftCache implements Cache {
// 硬列表,以避免GC, 初始化对象实例为LinkedList
// 用于保存一定数量强引用的值
private final Deque<Object> hardLinksToAvoidGarbageCollection;
// 软引用队列,当被垃圾收集器回收时,会将软引用对象放入此队列
private final ReferenceQueue<Object> queueOfGarbageCollectedEntries;
// 缓存委托对象,真正的缓存类
private final Cache delegate;
// 保存强引用值的数量,初始化为256
private int numberOfHardLinks;
public SoftCache(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();
// 将软引用作用到Value中
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) {
// 缓存数据结果
result = softReference.get();
if (result == null) {
// 获取的数据结果为null,说明该缓存项的value被垃圾回收器回收,删除缓存委托对象对应的缓存项
delegate.removeObject(key);
} else {
// 获取的数据结果不为null,将这个数据value放入强引用队列中,则value被强引用,则不会当内存不足时被垃圾收集器回收
// See #586 (and #335) modifications need more than a read lock
synchronized (hardLinksToAvoidGarbageCollection) {
// 存入结果value到强引用链表(numberOfHardLinks默认256元素),防止被垃圾回收
hardLinksToAvoidGarbageCollection.addFirst(result);
// 强引用队列会判断当前可容纳的数量,超过了就采用先进先出的策略进行移除
if (hardLinksToAvoidGarbageCollection.size() > numberOfHardLinks) {
// 移除的value再次变为弱引用的对象,当内存不足时会被垃圾收集器回收
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() {
SoftEntry sv;
// 软引用队列中存在数据时,移除缓存委托对象的缓存
while ((sv = (SoftEntry) queueOfGarbageCollectedEntries.poll()) != null) {
delegate.removeObject(sv.key);
}
}
// 继承了SoftReference,使得传进来的value转变成软引用
// 这里将其Value作为软引用,而不是用key,因为Key不能被回收,如果被移除的话,就会影响到整个体系,没有Key,就没有办法移除相关的值。
// 值被回收了,将软引用对象放到队列中,根据Key调用removeObject移除该关联的键和软引用对象
private static class SoftEntry extends SoftReference<Object> {
// 保存与value相关联的Key,因为一旦被垃圾收集器回收,则此软引用对象会被放到关联的引用队列中
// 这样就可以根据Key,移除该键值对
private final Object key;
SoftEntry(Object key, Object value, ReferenceQueue<Object> garbageCollectionQueue) {
super(value, garbageCollectionQueue);
this.key = key;
}
}
}
LoggingCache(日志功能装饰类,用于记录缓存的命中率):
// LoggingCache 源码
package org.apache.ibatis.cache.decorators;
// 日志功能装饰类,记录缓存命中率,输出命中率日志
public class LoggingCache implements Cache {
private final Log log;
// 缓存委托对象
private final Cache delegate;
// 缓存请求次数记录
protected int requests = 0;
// 缓存命中次数记录
protected int hits = 0;
public LoggingCache(Cache delegate) {
this.delegate = delegate;
this.log = LogFactory.getLog(getId());
}
@Override
public String getId() {
return delegate.getId();
}
@Override
public int getSize() {
return delegate.getSize();
}
@Override
public void putObject(Object key, Object object) {
delegate.putObject(key, object);
}
// 获取缓存数据
@Override
public Object getObject(Object key) {
// 统计请求次数
requests++;
final Object value = delegate.getObject(key);
if (value != null) {
// 统计缓存命中次数
hits++;
}
// 开启debug模式,打印缓存的命中率
if (log.isDebugEnabled()) {
log.debug("Cache Hit Ratio [" + getId() + "]: " + getHitRatio());
}
return value;
}
@Override
public Object removeObject(Object key) {
return delegate.removeObject(key);
}
@Override
public void clear() {
delegate.clear();
}
@Override
public int hashCode() {
return delegate.hashCode();
}
@Override
public boolean equals(Object obj) {
return delegate.equals(obj);
}
// 计算缓存的命中率
// 中率 = 命中次数 / 请求次数
private double getHitRatio() {
return (double) hits / (double) requests;
}
}
ScheduledCache(定时清空Cache类):
// ScheduledCache 源码
package org.apache.ibatis.cache.decorators;
// 定时清空Cache类
public class ScheduledCache implements Cache {
// 缓存委托对象
private final Cache delegate;
// 清空缓存数据时间间隔,初始化为间隔1小时
protected long clearInterval;
// 上一次清空的时间,毫秒
protected long lastClear;
public ScheduledCache(Cache delegate) {
this.delegate = delegate;
this.clearInterval = TimeUnit.HOURS.toMillis(1);
// 初始化时赋值当前系统时间
this.lastClear = System.currentTimeMillis();
}
// 设置清空时间间隔
public void setClearInterval(long clearInterval) {
this.clearInterval = clearInterval;
}
@Override
public String getId() {
return delegate.getId();
}
@Override
public int getSize() {
clearWhenStale();
return delegate.getSize();
}
@Override
public void putObject(Object key, Object object) {
clearWhenStale();
delegate.putObject(key, object);
}
@Override
public Object getObject(Object key) {
// 如果时间间隔超过缓存间隔时间直接返回null
return clearWhenStale() ? null : delegate.getObject(key);
}
@Override
public Object removeObject(Object key) {
clearWhenStale();
return delegate.removeObject(key);
}
@Override
// 清空所有缓存数据
public void clear() {
// 记录清空时间为当前时间
lastClear = System.currentTimeMillis();
delegate.clear();
}
@Override
public int hashCode() {
return delegate.hashCode();
}
@Override
public boolean equals(Object obj) {
return delegate.equals(obj);
}
// 判断是否需要触发清空数据
private boolean clearWhenStale() {
// 当前时间 - 上次清空时间 > 时间间隔 则进行缓存清空
if (System.currentTimeMillis() - lastClear > clearInterval) {
clear();
return true;
}
return false;
}
}
SerializedCache(序列化功能,将值序列化后存到缓存中):
// SerializedCache 源码
package org.apache.ibatis.cache.decorators;
// 序列化缓存数据功能,将存储数据序列化后存到缓存中,用于缓存返回一份实例的Copy,保存线程安全。
public class SerializedCache implements Cache {
// 缓存委托对象
private final Cache delegate;
public SerializedCache(Cache delegate) {
this.delegate = delegate;
}
@Override
public String getId() {
return delegate.getId();
}
@Override
public int getSize() {
return delegate.getSize();
}
@Override
public void putObject(Object key, Object object) {
if (object == null || object instanceof Serializable) {
// 调用serialize将数据序列化后在存储
delegate.putObject(key, serialize((Serializable) object));
} else {
throw new CacheException("SharedCache failed to make a copy of a non-serializable object: " + object);
}
}
@Override
public Object getObject(Object key) {
Object object = delegate.getObject(key);
// 获取数据,进行反序列化,生产一份copy对象
return object == null ? null : deserialize((byte[]) object);
}
@Override
public Object removeObject(Object key) {
return delegate.removeObject(key);
}
@Override
public void clear() {
delegate.clear();
}
@Override
public int hashCode() {
return delegate.hashCode();
}
@Override
public boolean equals(Object obj) {
return delegate.equals(obj);
}
// 序列化数据函数
private byte[] serialize(Serializable value) {
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos)) {
oos.writeObject(value);
oos.flush();
return bos.toByteArray();
} catch (Exception e) {
throw new CacheException("Error serializing object. Cause: " + e, e);
}
}
// 反序列化函数
private Serializable deserialize(byte[] value) {
SerialFilterChecker.check();
Serializable result;
try (ByteArrayInputStream bis = new ByteArrayInputStream(value);
ObjectInputStream ois = new CustomObjectInputStream(bis)) {
result = (Serializable) ois.readObject();
} catch (Exception e) {
throw new CacheException("Error deserializing object. Cause: " + e, e);
}
return result;
}
public static class CustomObjectInputStream extends ObjectInputStream {
public CustomObjectInputStream(InputStream in) throws IOException {
super(in);
}
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws ClassNotFoundException {
// 在序列化的类第一次序列化的时候才会被调用
// 遍历所支持的ClassLoader,加载对应的Class
return Resources.classForName(desc.getName());
}
}
}
SynchronizedCache(同步的缓存Cache类):
// SynchronizedCache 源码
package org.apache.ibatis.cache.decorators;
public class SynchronizedCache implements Cache {
private final Cache delegate;
public SynchronizedCache(Cache delegate) {
this.delegate = delegate;
}
@Override
public String getId() {
return delegate.getId();
}
@Override
public synchronized int getSize() {
return delegate.getSize();
}
@Override
public synchronized void putObject(Object key, Object object) {
delegate.putObject(key, object);
}
@Override
public synchronized Object getObject(Object key) {
return delegate.getObject(key);
}
@Override
public synchronized Object removeObject(Object key) {
return delegate.removeObject(key);
}
@Override
public synchronized void clear() {
delegate.clear();
}
@Override
public int hashCode() {
return delegate.hashCode();
}
@Override
public boolean equals(Object obj) {
return delegate.equals(obj);
}
}