Mybatis缓存模块源码分析
Mybatis中提供了缓存机制,而且有很多中不同策略的缓存,如LRU,FIFO,Schedule等等,那么Mybatis如何设计这些功能繁多的缓存呢?
1、装饰者模式设计缓存
1.1 提供统一的Cache接口:
public interface Cache {
String getId();
void putObject(Object key, Object value);
Object getObject(Object key);
Object removeObject(Object key);
void clear();
int getSize();
default ReadWriteLock getReadWriteLock() {
return null;
}
}
1.2 提供一个最基本的实现,底层采用HashMap存储
/**
* Cache最基本的实现,底层采用hashmap来实现
* @author Clinton Begin
*/
public class PerpetualCache implements Cache {
private final String id;
private final Map<Object, Object> cache = new HashMap<>();
// ....省略部分方法
@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();
}
}
1.3 其它功能的缓存,有LRU,FIFI,Blocking.等等,每个都提供了一个实现类,类中含有一个Cache对象,对这个Cache对象做增强,以FIFO为例:
/**
* FIFO (first in, first out) cache decorator.
* 采用LinkedList赋值实现,双端队列,队列中存储缓存的key,当容量满时,删除第一个key实现FIFO
* @author Clinton Begin,
*/
public class FifoCache implements Cache {
private final Cache delegate;
private final Deque<Object> keyList;
private int size;
public FifoCache(Cache delegate) {
this.delegate = delegate;
this.keyList = new LinkedList<>();
this.size = 1024;
}
@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) {
keyList.addLast(key);
if (keyList.size() > size) {
Object oldestKey = keyList.removeFirst();
delegate.removeObject(oldestKey);
}
}
}
2、BlockingCache经典代码分析
private final ConcurrentHashMap<Object, ReentrantLock> locks;
// 解决缓存雪崩问题
@Override
public Object getObject(Object key) {
acquireLock(key);
Object value = delegate.getObject(key);
if (value != null) {
releaseLock(key);
}
return value;
}
BlockingCache中有这段代码,可以保证当发生缓存雪崩(缓存中没有数据,需要去数据库中查),在一个高并发系统中,如果不进行控制,第一批请求全部涌入数据库,很可能造成数据库挂掉。有的解决方案也是加锁,但是把缓存作为锁,这样的话锁粒度过大,并发量不高。但这里采取每个key一把锁,大大降低了锁的粒度。