缓存
Cache接口
mybatis中包含一级缓存和二级缓存,在本质上是一样的,使用的都是Cache接口的实现。
Cache接口定义了所有缓存的基本行为。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public interface Cache { String getId(); void putObject(Object var1, Object var2); Object getObject(Object var1); Object removeObject(Object var1); void clear(); int getSize(); ReadWriteLock getReadWriteLock(); }
|
对于每一个 namespace 都会创建一个缓存的实例,Cache 实现类的构造方法都必须传入一个 String 类型的ID,Mybatis自身的实现类都使用 namespace 作为 ID
下面介绍一下Cache接口的实现
PerpetualCache
使用HashMap进行存储
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| 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对应的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
| 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) { this.acquireLock(key); Object value = this.delegate.getObject(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是先进先出版本的装饰器如果缓存项的个数达到上限,会将缓存中最先入队的缓存项删除。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| 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; }
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按照最近最少使用算法进行缓存清理的装饰器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| public class LruCache implements Cache { private final Cache delegate; 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) { this.keyMap = new LinkedHashMap<Object, Object>(size, 0.75F, true) { private static final long serialVersionUID = 4267176411845948333L; 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| 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(); 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| 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的命中次数和访问次数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| 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周期性清理缓存,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| 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进行序列化的功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
| 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,添加了同步功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| 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生成一个哈希码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
| 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; private final int multiplier; private int hashcode; private long checksum; private int count; 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(); } 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); }
}
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; } }
|