文章目录
缓存
Cache接口
mybatis中包含一级缓存和二级缓存,在本质上是一样的,使用的都是Cache接口的实现。
Cache接口定义了所有缓存的基本行为。
public interface Cache {
// 缓存对象的id
String getId();
// 向缓存中添加数据,key一般为CacheKey
void putObject(Object var1, Object var2);
// 通过key来获取缓存值
Object getObject(Object var1);
// 通过key删除对应的缓存
Object removeObject(Object var1);
// 清除缓存
void clear();
// 获取缓存中数据的大小
int getSize();
// 3.2.6之后不在使用
ReadWriteLock getReadWriteLock();
}
对于每一个 namespace 都会创建一个缓存的实例,Cache 实现类的构造方法都必须传入一个 String 类型的ID,Mybatis自身的实现类都使用 namespace 作为 ID
下面介绍一下Cache接口的实现
PerpetualCache
使用HashMap进行存储
public class PerpetualCache implements Cache {
private final String id;
// 存储缓存数据
private final 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 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();
}
}
}
BlockingCache
BlockingCache是阻塞版本的缓存装饰器,保证只有一个线程到数据库中查找指定key对应的数据
public class BlockingCache implements Cache {
//阻塞超时时长
private long timeout;
// 被装饰的底层Cache对象
private final Cache delegate;
//每个key对应一个ReetrantLock
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);
//缓存中有key对应的缓存项才会释放锁,否则会一直持有锁
if (value != null) {
this.releaseLock(key);
}
return value;
}
public Object removeObject(Object key) {
this.releaseLock(key);
return null;
}
public void clear() {
this.delegate.clear();
}
private ReentrantLock getLockForKey(Object key) {
return (ReentrantLock)this.locks.computeIfAbsent(key, (k) -> {
return new ReentrantLock();
});
}
private void acquireLock(Object key) {
Lock lock = this.getLockForKey(key);
if (this.timeout > 0L) {
try {
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;
}
}
FifoCache
FifoCache是先进先出版本的装饰器如果缓存项的个数达到上限,会将缓存中最先入队的缓存项删除。
public class FifoCache implements Cache {
private final Cache delegate;
// 根据key进入缓存的先后顺序
private final Deque<Object> keyList;
// 记录缓存的上限
private int size;
public FifoCache(Cache delegate) {
this.delegate = delegate;
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) {
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();
}
//检测并清理缓存
private void cycleKeyList(Object key) {
this.keyList.addLast(key);
if (this.keyList.size() > this.size) {
Object oldestKey = this.keyList.removeFirst();
this.delegate.removeObject(oldestKey);
}
}
}
LruCache
LruCache按照最近最少使用算法进行缓存清理的装饰器。
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;
this.setSize(1024);
}
public String getId() {
return this.delegate.getId();
}
public int getSize() {
return this.delegate.getSize();
}
public void setSize(final int size) {
//LinkedHashMap的第三个参数true表示记录的顺序是access-order,get方法会改变顺序记录
this.keyMap = new LinkedHashMap<Object, Object>(size, 0.75F, true) {
private static final long serialVersionUID = 4267176411845948333L;
// 调用put方法时会调用该方法
protected boolean removeEldestEntry(Entry<Object, Object> eldest) {
boolean tooBig = this.size() > size;
if (tooBig) {
LruCache.this.eldestKey = eldest.getKey();
}
return tooBig;
}
};
}
public void putObject(Object key, Object value) {
this.delegate.putObject(key, value);
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();
}
private void cycleKeyList(Object key) {
this.keyMap.put(key, key);
if (this.eldestKey != null) {
this.delegate.removeObject(this.eldestKey);
this.eldestKey = null;
}
}
}
SoftCache
public class SoftCache implements Cache {
// 最近使用的一部分缓存不会被GC掉,将值放到该队列中(强引用指向其value)
private final Deque<Object> hardLinksToAvoidGarbageCollection;
//引用队列,用于记录已经被GC回收的缓存项所对应的SoftEntry对象
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();
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) {
this.delegate.removeObject(key);
} else {
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() {
synchronized(this.hardLinksToAvoidGarbageCollection) {
this.hardLinksToAvoidGarbageCollection.clear();
}
this.removeGarbageCollectedItems();
this.delegate.clear();
}
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;
}
}
}
WeakCache
public class WeakCache implements Cache {
private final Deque<Object> hardLinksToAvoidGarbageCollection;
private final ReferenceQueue<Object> queueOfGarbageCollectedEntries;
private final Cache delegate;
private int numberOfHardLinks;
public WeakCache(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();
this.delegate.putObject(key, new WeakCache.WeakEntry(key, value, this.queueOfGarbageCollectedEntries));
}
public Object getObject(Object key) {
Object result = null;
WeakReference<Object> weakReference = (WeakReference)this.delegate.getObject(key);
if (weakReference != null) {
result = weakReference.get();
if (result == null) {
this.delegate.removeObject(key);
} else {
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() {
this.hardLinksToAvoidGarbageCollection.clear();
this.removeGarbageCollectedItems();
this.delegate.clear();
}
private void removeGarbageCollectedItems() {
WeakCache.WeakEntry sv;
while((sv = (WeakCache.WeakEntry)this.queueOfGarbageCollectedEntries.poll()) != null) {
this.delegate.removeObject(sv.key);
}
}
private static class WeakEntry extends WeakReference<Object> {
private final Object key;
private WeakEntry(Object key, Object value, ReferenceQueue<Object> garbageCollectionQueue) {
super(value, garbageCollectionQueue);
this.key = key;
}
}
}
LoggingCache
LoggingCache在Cache的基础上提供了日志功能,通过hits和request字段记录了Cache的命中次数和访问次数。
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(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);
}
public Object getObject(Object key) {
++this.requests;
Object value = this.delegate.getObject(key);
if (value != null) {
++this.hits;
}
if (this.log.isDebugEnabled()) {
this.log.debug("Cache Hit Ratio [" + this.getId() + "]: " + this.getHitRatio());
}
return value;
}
public Object removeObject(Object key) {
return this.delegate.removeObject(key);
}
public void clear() {
this.delegate.clear();
}
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;
}
}
ScheduledCache
ScheduledCache周期性清理缓存,
public class ScheduledCache implements Cache {
private final Cache delegate;
// 记录两次缓存清理的时间间隔,默认一小时
protected long clearInterval;
// 记录最后一次清理的时间戳
protected long lastClear;
public ScheduledCache(Cache delegate) {
this.delegate = delegate;
this.clearInterval = TimeUnit.HOURS.toMillis(1L);
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 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;
}
}
}
SerializedCache
SerializedCache提供了将value进行序列化的功能
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 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();
Throwable var3 = null;
Object var6;
try {
ObjectOutputStream oos = new ObjectOutputStream(bos);
Throwable var5 = null;
try {
oos.writeObject(value);
oos.flush();
var6 = bos.toByteArray();
} catch (Throwable var31) {
var6 = var31;
var5 = var31;
throw var31;
} finally {
if (oos != null) {
if (var5 != null) {
try {
oos.close();
} catch (Throwable var30) {
var5.addSuppressed(var30);
}
} else {
oos.close();
}
}
}
} catch (Throwable var33) {
var3 = var33;
throw var33;
} finally {
if (bos != null) {
if (var3 != null) {
try {
bos.close();
} catch (Throwable var29) {
var3.addSuppressed(var29);
}
} else {
bos.close();
}
}
}
return (byte[])var6;
} catch (Exception var35) {
throw new CacheException("Error serializing object. Cause: " + var35, var35);
}
}
private Serializable deserialize(byte[] value) {
try {
ByteArrayInputStream bis = new ByteArrayInputStream(value);
Throwable var4 = null;
Serializable result;
try {
ObjectInputStream ois = new SerializedCache.CustomObjectInputStream(bis);
Throwable var6 = null;
try {
result = (Serializable)ois.readObject();
} catch (Throwable var31) {
var6 = var31;
throw var31;
} finally {
if (ois != null) {
if (var6 != null) {
try {
ois.close();
} catch (Throwable var30) {
var6.addSuppressed(var30);
}
} else {
ois.close();
}
}
}
} catch (Throwable var33) {
var4 = var33;
throw var33;
} finally {
if (bis != null) {
if (var4 != null) {
try {
bis.close();
} catch (Throwable var29) {
var4.addSuppressed(var29);
}
} else {
bis.close();
}
}
}
return result;
} catch (Exception var35) {
throw new CacheException("Error deserializing object. Cause: " + var35, var35);
}
}
public static class CustomObjectInputStream extends ObjectInputStream {
public CustomObjectInputStream(InputStream in) throws IOException {
super(in);
}
protected Class<?> resolveClass(ObjectStreamClass desc) throws ClassNotFoundException {
return Resources.classForName(desc.getName());
}
}
}
SynchronizedCache
SynchronizedCache为每个方法添加synchronized,添加了同步功能
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);
}
}
CacheKey
在Cache中唯一确定一个缓存项使用的key为CacheKey
CacheKey的组成部分
- MappedStatement的id
- 指定查询结果集的范围,也就是RowBounds.offset和RowBounds.limit
- 查询所使用的sql语句,boundSql.getSql(),包含?占位符
- 用户传递的实际参数
CacheKey就是mappedStementId + offset + limit + SQL + queryParams + environment生成一个哈希码
public class CacheKey implements Cloneable, Serializable {
private static final long serialVersionUID = 1146682552656046210L;
public static final CacheKey NULL_CACHE_KEY = new CacheKey() {
public void update(Object object) {
throw new CacheException("Not allowed to update a null cache key instance.");
}
public void updateAll(Object[] objects) {
throw new CacheException("Not allowed to update a null cache key instance.");
}
};
private static final int DEFAULT_MULTIPLIER = 37;
private static final int DEFAULT_HASHCODE = 17;
// 参与计算hashcode,默认为37
private final int multiplier;
// CacheKey的hashcode,默认为17
private int hashcode;
//检验和
private long checksum;
// updateList集合的个数
private int count;
// 由该集合中的所有对象共同决定两个CacheKey是否相同
private List<Object> updateList;
public CacheKey() {
this.hashcode = 17;
this.multiplier = 37;
this.count = 0;
this.updateList = new ArrayList();
}
public CacheKey(Object[] objects) {
this();
this.updateAll(objects);
}
public int getUpdateCount() {
return this.updateList.size();
}
// 向updateList中添加对象时,使用的是update方法
public void update(Object object) {
int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object);
++this.count;
this.checksum += (long)baseHashCode;
baseHashCode *= this.count;
this.hashcode = this.multiplier * this.hashcode + baseHashCode;
this.updateList.add(object);
}
public void updateAll(Object[] objects) {
Object[] var2 = objects;
int var3 = objects.length;
for(int var4 = 0; var4 < var3; ++var4) {
Object o = var2[var4];
this.update(o);
}
}
// 判断两个cacheKey是否相等
public boolean equals(Object object) {
if (this == object) {
return true;
} else if (!(object instanceof CacheKey)) {
return false;
} else {
CacheKey cacheKey = (CacheKey)object;
if (this.hashcode != cacheKey.hashcode) {
return false;
} else if (this.checksum != cacheKey.checksum) {
return false;
} else if (this.count != cacheKey.count) {
return false;
} else {
for(int i = 0; i < this.updateList.size(); ++i) {
Object thisObject = this.updateList.get(i);
Object thatObject = cacheKey.updateList.get(i);
if (!ArrayUtil.equals(thisObject, thatObject)) {
return false;
}
}
return true;
}
}
}
public int hashCode() {
return this.hashcode;
}
public String toString() {
StringJoiner returnValue = new StringJoiner(":");
returnValue.add(String.valueOf(this.hashcode));
returnValue.add(String.valueOf(this.checksum));
this.updateList.stream().map(ArrayUtil::toString).forEach(returnValue::add);
return returnValue.toString();
}
public CacheKey clone() throws CloneNotSupportedException {
CacheKey clonedCacheKey = (CacheKey)super.clone();
clonedCacheKey.updateList = new ArrayList(this.updateList);
return clonedCacheKey;
}
}
参考文献