一.前言

Mybatis缓存的功能由根接口Cache(org.apache.ibatis.cache.Cache)定义,整个体系采用装饰器设计模式。

二.Cache结构介绍

1.Cache的包目录结构

MyBatis源码分析之缓存Cache

2.Cahce的接口类图关系

MyBatis源码分析之缓存Cache
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同时获取缓存时,只有一个线程会获取锁从而获取数据,另外一个则被阻塞等待。
MyBatis源码分析之缓存Cache
代码实现:

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

相关文章: