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一把鎖,大大降低了鎖的粒度。