一.前言
Mybatis缓存的功能由根接口Cache(org.apache.ibatis.cache.Cache)定义,整个体系采用装饰器设计模式。
二.Cache结构介绍
1.Cache的包目录结构
2.Cahce的接口类图关系
PerpetualCache介绍:
作为为最基础的缓存类,PerpetualCache 采用基于 HashMap的本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该Session中的所有 Cache 就将清空。
decorators装饰器介绍:
decorators装饰者包括10个不同的装饰对象,用于装饰PerpetualCache,增强其功能
- BlockingCache:阻塞版本的缓存装饰器,它保证只有一个线程到数据库中查找指定key对应的数据
- FifoCache:先进先出算法,缓存回收策略
- LoggingCache:输出缓存命中的日志信息
- LruCache:最近最少使用算法,缓存回收策略
- ScheduledCache:调度缓存,负责定时清空缓存
- SerializedCache:缓存序列化和反序列化存储
- SoftCache:基于软引用实现的缓存管理策略
- SynchronizedCache:同步的缓存装饰器,用于防止多线程并发访问
- WeakCache:基于弱引用实现的缓存管理策略
- TransactionalCache:事务性的缓存
Cache对象之间的引用顺序为:
SynchronizedCache–>LoggingCache–>SerializedCache–>ScheduledCache–>LruCache–>PerpetualCache
所有的缓存对象的操作与维护都是由Executor器执行来完成的,一级缓存由BaseExecutor(包含SimpleExecutor、ReuseExecutor、BatchExecutor三个子类)负责维护,二级缓存由CachingExecutor负责维护。因此需要注意的是:配置了二级缓存不代表mybatis就会使用二级缓存,还需要确保在创建SqlSession的过程中,mybatis创建是CachingExecutor类型的执行器。
三.Cache接口
public interface Cache {
// 该缓存对象的Id
String getId();
//向缓存中添加数据
void putObject(Object key, Object value);
//根据key 在缓存中查找结果
Object getObject(Object key);
// 删除key 对应的缓存项
Object removeObject(Object key);
// 清空缓存
void clear();
//缓存项个数
int getSize();
// 获取读写锁
ReadWriteLock getReadWriteLock();
}
四.Cache实现
1.PerpetualCache
基于map对象简单的进行存储
public class PerpetualCache implements Cache {
private final String id;
private Map<Object, Object> cache = new HashMap();
public PerpetualCache(String id) {
this.id = id;
}
public String getId() {
return this.id;
}
public int getSize() {
return this.cache.size();
}
public void putObject(Object key, Object value) {
this.cache.put(key, value);
}
public Object getObject(Object key) {
return this.cache.get(key);
}
public Object removeObject(Object key) {
return this.cache.remove(key);
}
public void clear() {
this.cache.clear();
}
public ReadWriteLock getReadWriteLock() {
return null;
}
public boolean equals(Object o) {
if (this.getId() == null) {
throw new CacheException("Cache instances require an ID.");
} else if (this == o) {
return true;
} else if (!(o instanceof Cache)) {
return false;
} else {
Cache otherCache = (Cache)o;
return this.getId().equals(otherCache.getId());
}
}
public int hashCode() {
if (this.getId() == null) {
throw new CacheException("Cache instances require an ID.");
} else {
return this.getId().hashCode();
}
}
}
2.BlockingCache
BlockingCache 是阻塞版本的缓存装饰器,它保证只有一个线程到数据库中查找指定key对应的数据。如下图:线程A与线程B同时获取缓存时,只有一个线程会获取锁从而获取数据,另外一个则被阻塞等待。
代码实现:
public class BlockingCache implements Cache {
private long timeout;
private final Cache delegate;
private final ConcurrentHashMap<Object, ReentrantLock> locks;
public BlockingCache(Cache delegate) {
this.delegate = delegate;
this.locks = new ConcurrentHashMap();
}
public String getId() {
return this.delegate.getId();
}
public int getSize() {
return this.delegate.getSize();
}
// 向缓存中添加缓存
public void putObject(Object key, Object value) {
try {
this.delegate.putObject(key, value);
} finally {
this.releaseLock(key);
}
}
public Object getObject(Object key) {
//// 获取key对应的锁
this.acquireLock(key);
//// 查询key对应的值
Object value = this.delegate.getObject(key);
if (value != null) {
// // 如果从缓存(PrepetualCache是用HashMap实现的)中查找到,则释放锁,否则继续持有锁
this.releaseLock(key);
}
return value;
}
public Object removeObject(Object key) {
this.releaseLock(key);
return null;
}
public void clear() {
this.delegate.clear();
}
public ReadWriteLock getReadWriteLock() {
return null;
}
//通过key获取锁lock
private ReentrantLock getLockForKey(Object key) {
ReentrantLock lock = new ReentrantLock();
ReentrantLock previous = (ReentrantLock)this.locks.putIfAbsent(key, lock);
return previous == null ? lock : previous;
}
//通过key获取锁
private void acquireLock(Object key) {
//尝试获取对应的锁
Lock lock = this.getLockForKey(key);
if (this.timeout > 0L) {
try {
//在timeout时间内去获取锁
boolean acquired = lock.tryLock(this.timeout, TimeUnit.MILLISECONDS);
if (!acquired) {
throw new CacheException("Couldn't get a lock in " + this.timeout + " for the key " + key + " at the cache " + this.delegate.getId());
}
} catch (InterruptedException var4) {
throw new CacheException("Got interrupted while trying to acquire lock for key " + key, var4);
}
} else {
lock.lock();
}
}
//释放锁
private void releaseLock(Object key) {
ReentrantLock lock = (ReentrantLock)this.locks.get(key);
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
//获取超时时间
public long getTimeout() {
return this.timeout;
}
//设置超时时间
public void setTimeout(long timeout) {
this.timeout = timeout;
}
}
3.FifoCache
先进先出缓存回收策略装饰器
public class FifoCache implements Cache {
private final Cache delegate;
//不限容量的LinkedList
private final Deque<Object> keyList;
//// 记录了缓存页的上限,超过该值需要清理缓存(FIFO)
private int size;
public FifoCache(Cache delegate) {
this.delegate = delegate;
//创建LinkedList集合对象
this.keyList = new LinkedList();
this.size = 1024;
}
public String getId() {
return this.delegate.getId();
}
public int getSize() {
return this.delegate.getSize();
}
public void setSize(int size) {
this.size = size;
}
//存放缓存数据,验证是否超过限制长度,再添加数据
public void putObject(Object key, Object value) {
//调用cycleKeyList进行一次验证
this.cycleKeyList(key);
this.delegate.putObject(key, value);
}
public Object getObject(Object key) {
return this.delegate.getObject(key);
}
public Object removeObject(Object key) {
return this.delegate.removeObject(key);
}
public void clear() {
this.delegate.clear();
this.keyList.clear();
}
public ReadWriteLock getReadWriteLock() {
return null;
}
//如果keyList超过限制长度,则进行回收
private void cycleKeyList(Object key) {
this.keyList.addLast(key);
if (this.keyList.size() > this.size) {
//长度超过了指定大小,则移除第一个元素
Object oldestKey = this.keyList.removeFirst();
this.delegate.removeObject(oldestKey);
}
}
}
4.LoggingCache
日志功能,装饰类,用于记录缓存的命中率,如果开启了DEBUG模式,则会输出命中率日志。
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;
//定义日志log
this.log = LogFactory.getLog(this.getId());
}
public String getId() {
return this.delegate.getId();
}
public int getSize() {
return this.delegate.getSize();
}
public void putObject(Object key, Object object) {
this.delegate.putObject(key, object);
}
//通过key获取缓存
public Object getObject(Object key) {
++this.requests;
Object value = this.delegate.getObject(key);
if (value != null) {
++this.hits;
}
//是否开启dubug模式
if (this.log.isDebugEnabled()) {
//如果开启,则输出debug日志
this.log.debug("Cache Hit Ratio [" + this.getId() + "]: " + this.getHitRatio());
}
return value;
}
//通过key移除缓存
public Object removeObject(Object key) {
return this.delegate.removeObject(key);
}
public void clear() {
this.delegate.clear();
}
public ReadWriteLock getReadWriteLock() {
return null;
}
public int hashCode() {
return this.delegate.hashCode();
}
public boolean equals(Object obj) {
return this.delegate.equals(obj);
}
//计算命中率
private double getHitRatio() {
return (double)this.hits / (double)this.requests;
}
}
5.LruCache
LruCache 是按照LRU 算法进行缓存清理的装饰器,在需要清理缓存时,它会清除最近最少使用的缓存项。
public class LruCache implements Cache {
private final Cache delegate;
//LinkedHashMap有序集合对象
private Map<Object, Object> keyMap;
private Object eldestKey;
public LruCache(Cache delegate) {
this.delegate = delegate;
// 设置 map 默认大小
this.setSize(1024);
}
public String getId() {
return this.delegate.getId();
}
public int getSize() {
return this.delegate.getSize();
}
public void setSize(final int size) {
// 设置其 capacity 为size, 其 factor 为.75F
this.keyMap = new LinkedHashMap<Object, Object>(size, 0.75F, true) {
private static final long serialVersionUID = 4267176411845948333L;
// 覆盖该方法,当每次往该map 中put 时数据时,如该方法返回 True,便移除该map中使用最少的Entry
// 其参数 eldest 为当前最老的 Entry
protected boolean removeEldestEntry(Entry<Object, Object> eldest) {
boolean tooBig = this.size() > size;
if (tooBig) {
//记录当前最旧的缓存数据的 Key 值
LruCache.this.eldestKey = eldest.getKey();
}
return tooBig;
}
};
}
public void putObject(Object key, Object value) {
//存放key value
this.delegate.putObject(key, value);
// 调用移除最旧的 key
this.cycleKeyList(key);
}
public Object getObject(Object key) {
this.keyMap.get(key);
return this.delegate.getObject(key);
}
public Object removeObject(Object key) {
return this.delegate.removeObject(key);
}
//清除
public void clear() {
this.delegate.clear();
this.keyMap.clear();
}
public ReadWriteLock getReadWriteLock() {
return null;
}
private void cycleKeyList(Object key) {
this.keyMap.put(key, key);
if (this.eldestKey != null) {
//查看是否有 eldestKey, 有的话就调用 removeObject ,将该key从cache中移除
this.delegate.removeObject(this.eldestKey);
this.eldestKey = null;
}
}
}
6.ScheduledCache
定时清空Cache,但是并没有开始一个定时任务,而是在使用Cache的时候,才去检查时间是否到了。
public class ScheduledCache implements Cache {
private final Cache delegate;
// 清除的时间间隔
protected long clearInterval;
// 最后一次清除的时间
protected long lastClear;
public ScheduledCache(Cache delegate) {
this.delegate = delegate;
//初始化
this.clearInterval = 3600000L;
this.lastClear = System.currentTimeMillis();
}
public void setClearInterval(long clearInterval) {
this.clearInterval = clearInterval;
}
public String getId() {
return this.delegate.getId();
}
public int getSize() {
this.clearWhenStale();
return this.delegate.getSize();
}
//存放时候,判断是否需要清除
public void putObject(Object key, Object object) {
this.clearWhenStale();
this.delegate.putObject(key, object);
}
public Object getObject(Object key) {
return this.clearWhenStale() ? null : this.delegate.getObject(key);
}
//移除,同时判断是否有过期的缓存需要清除
public Object removeObject(Object key) {
this.clearWhenStale();
return this.delegate.removeObject(key);
}
public void clear() {
this.lastClear = System.currentTimeMillis();
this.delegate.clear();
}
public ReadWriteLock getReadWriteLock() {
return null;
}
public int hashCode() {
return this.delegate.hashCode();
}
public boolean equals(Object obj) {
return this.delegate.equals(obj);
}
private boolean clearWhenStale() {
if (System.currentTimeMillis() - this.lastClear > this.clearInterval) {
//大于时间间隔,清除缓存
this.clear();
return true;
} else {
return false;
}
}
}
7.SerializedCache
对 Cache的数据 put或get 进行序列化及反序列化处理
public class SerializedCache implements Cache {
private final Cache delegate;
public SerializedCache(Cache delegate) {
this.delegate = delegate;
}
public String getId() {
return this.delegate.getId();
}
public int getSize() {
return this.delegate.getSize();
}
//存放数据时,进行数据序列化
public void putObject(Object key, Object object) {
if (object != null && !(object instanceof Serializable)) {
throw new CacheException("SharedCache failed to make a copy of a non-serializable object: " + object);
} else {
this.delegate.putObject(key, this.serialize((Serializable)object));
}
}
//获取数据时进行反***获取
public Object getObject(Object key) {
Object object = this.delegate.getObject(key);
return object == null ? null : this.deserialize((byte[])((byte[])object));
}
public Object removeObject(Object key) {
return this.delegate.removeObject(key);
}
public void clear() {
this.delegate.clear();
}
public ReadWriteLock getReadWriteLock() {
return null;
}
public int hashCode() {
return this.delegate.hashCode();
}
public boolean equals(Object obj) {
return this.delegate.equals(obj);
}
//序列化规则
private byte[] serialize(Serializable value) {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(value);
oos.flush();
oos.close();
return bos.toByteArray();
} catch (Exception var4) {
throw new CacheException("Error serializing object. Cause: " + var4, var4);
}
}
//反序列化规则
private Serializable deserialize(byte[] value) {
try {
ByteArrayInputStream bis = new ByteArrayInputStream(value);
ObjectInputStream ois = new SerializedCache.CustomObjectInputStream(bis);
Serializable result = (Serializable)ois.readObject();
ois.close();
return result;
} catch (Exception var5) {
throw new CacheException("Error deserializing object. Cause: " + var5, var5);
}
}
public static class CustomObjectInputStream extends ObjectInputStream {
public CustomObjectInputStream(InputStream in) throws IOException {
super(in);
}
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
return Resources.classForName(desc.getName());
}
}
}
8.SoftCache
软引用回收策略,软引用只有当内存不足时才会被垃圾收集器回收。这里的实现机制中,使用了一个链表来保证一定数量的值即使内存不足也不会被回收,但是没有保存在该链表的值则有可能会被回收。
在WeakHashMap中,可以看到是将引用应用到Key的,当Key被回收后,则移除相关的Value。但是这里是将其应用到Value中,因为Key不能被回收,如果被移除的话,就会影响到整个体系,最底层的实现使用HashMap实现的,没有Key,就没有办法移除相关的值。反过来,值被回收了,将软引用对象放到队列中,可以根据Key调用removeObject移除该关联的键和软引用对象。
public class SoftCache implements Cache {
// 用于保存一定数量强引用的值
private final Deque<Object> hardLinksToAvoidGarbageCollection;
// 引用队列,当被垃圾收集器回收时,会将软引用对象放入此队列
private final ReferenceQueue<Object> queueOfGarbageCollectedEntries;
private final Cache delegate;
// 保存强引用值的数量
private int numberOfHardLinks;
public SoftCache(Cache delegate) {
//初始化
this.delegate = delegate;
this.numberOfHardLinks = 256;
this.hardLinksToAvoidGarbageCollection = new LinkedList();
this.queueOfGarbageCollectedEntries = new ReferenceQueue();
}
public String getId() {
return this.delegate.getId();
}
public int getSize() {
this.removeGarbageCollectedItems();
return this.delegate.getSize();
}
public void setSize(int size) {
this.numberOfHardLinks = size;
}
public void putObject(Object key, Object value) {
// 移除被垃圾收集器回收的键值
this.removeGarbageCollectedItems();
// 将软引用作用到Value中
this.delegate.putObject(key, new SoftCache.SoftEntry(key, value, this.queueOfGarbageCollectedEntries));
}
public Object getObject(Object key) {
Object result = null;
SoftReference<Object> softReference = (SoftReference)this.delegate.getObject(key);
if (softReference != null) {
result = softReference.get();
if (result == null) {
// 该值被垃圾收集器回收,移除掉该key值
this.delegate.removeObject(key);
} else {
Deque var4 = this.hardLinksToAvoidGarbageCollection;
synchronized(this.hardLinksToAvoidGarbageCollection) {
this.hardLinksToAvoidGarbageCollection.addFirst(result);
if (this.hardLinksToAvoidGarbageCollection.size() > this.numberOfHardLinks) {
// 超出容量,则移除最先保存的引用
this.hardLinksToAvoidGarbageCollection.removeLast();
}
}
}
}
return result;
}
// 移除被垃圾收集器回收的键值
public Object removeObject(Object key) {
this.removeGarbageCollectedItems();
return this.delegate.removeObject(key);
}
public void clear() {
Deque var1 = this.hardLinksToAvoidGarbageCollection;
synchronized(this.hardLinksToAvoidGarbageCollection) {
// 清空该队列,否则即使下面调用clear,其Map清空了,但是部分值保留有引用,垃圾收集器也不会回收,会造成短暂的内存泄漏。
this.hardLinksToAvoidGarbageCollection.clear();
}
this.removeGarbageCollectedItems();
this.delegate.clear();
}
public ReadWriteLock getReadWriteLock() {
return null;
}
private void removeGarbageCollectedItems() {
SoftCache.SoftEntry sv;
while((sv = (SoftCache.SoftEntry)this.queueOfGarbageCollectedEntries.poll()) != null) {
this.delegate.removeObject(sv.key);
}
}
private static class SoftEntry extends SoftReference<Object> {
private final Object key;
SoftEntry(Object key, Object value, ReferenceQueue<Object> garbageCollectionQueue) {
super(value, garbageCollectionQueue);
this.key = key;
}
}
}
9.SynchronizedCache
用于控制 ReadWriteLock,避免并发时所产生的线程安全问题
public class SynchronizedCache implements Cache {
private final Cache delegate;
public SynchronizedCache(Cache delegate) {
this.delegate = delegate;
}
public String getId() {
return this.delegate.getId();
}
public synchronized int getSize() {
return this.delegate.getSize();
}
public synchronized void putObject(Object key, Object object) {
this.delegate.putObject(key, object);
}
public synchronized Object getObject(Object key) {
return this.delegate.getObject(key);
}
public synchronized Object removeObject(Object key) {
return this.delegate.removeObject(key);
}
public synchronized void clear() {
this.delegate.clear();
}
public int hashCode() {
return this.delegate.hashCode();
}
public boolean equals(Object obj) {
return this.delegate.equals(obj);
}
public ReadWriteLock getReadWriteLock() {
return null;
}
}
10.TransactionalCache
事务缓存,在提交的时候,才真正的放到Cache中,或者回滚的时候清除掉
public class TransactionalCache implements Cache {
private static final Log log = LogFactory.getLog(TransactionalCache.class);
private final Cache delegate;
private boolean clearOnCommit;
private final Map<Object, Object> entriesToAddOnCommit;
private final Set<Object> entriesMissedInCache;
public TransactionalCache(Cache delegate) {
this.delegate = delegate;
this.clearOnCommit = false;
this.entriesToAddOnCommit = new HashMap();
this.entriesMissedInCache = new HashSet();
}
public String getId() {
return this.delegate.getId();
}
public int getSize() {
return this.delegate.getSize();
}
public Object getObject(Object key) {
Object object = this.delegate.getObject(key);
if (object == null) {
this.entriesMissedInCache.add(key);
}
return this.clearOnCommit ? null : object;
}
public ReadWriteLock getReadWriteLock() {
return null;
}
public void putObject(Object key, Object object) {
this.entriesToAddOnCommit.put(key, object);
}
public Object removeObject(Object key) {
return null;
}
public void clear() {
this.clearOnCommit = true;
this.entriesToAddOnCommit.clear();
}
public void commit() {
if (this.clearOnCommit) {
// 提交事务时需要先清空,则清空缓存
this.delegate.clear();
}
this.flushPendingEntries();
this.reset();
}
public void rollback() {
this.unlockMissedEntries();
this.reset();
}
private void reset() {
this.clearOnCommit = false;
this.entriesToAddOnCommit.clear();
this.entriesMissedInCache.clear();
}
private void flushPendingEntries() {
Iterator var1 = this.entriesToAddOnCommit.entrySet().iterator();
while(var1.hasNext()) {
Entry<Object, Object> entry = (Entry)var1.next();
this.delegate.putObject(entry.getKey(), entry.getValue());
}
var1 = this.entriesMissedInCache.iterator();
while(var1.hasNext()) {
Object entry = var1.next();
if (!this.entriesToAddOnCommit.containsKey(entry)) {
this.delegate.putObject(entry, (Object)null);
}
}
}
private void unlockMissedEntries() {
Iterator var1 = this.entriesMissedInCache.iterator();
while(var1.hasNext()) {
Object entry = var1.next();
try {
this.delegate.removeObject(entry);
} catch (Exception var4) {
log.warn("Unexpected exception while notifiying a rollback to the cache adapter.Consider upgrading your cache adapter to the latest version. Cause: " + var4);
}
}
}
}
11.WeakCache
弱引用回收策略,弱引用的对象一旦被垃圾收集器发现,则会被回收,无论内存是否足够。这里的实现和上面的软引用类似,除了使用WeakReference替换掉SoftReference,其它基本一样。
参考:
https://my.oschina.net/lixin91/blog/620068
https://segmentfault.com/a/1190000014873903
https://www.cnblogs.com/wei-zw/p/8903977.html