Mybatis Cache包分析

本文详细介绍了多种缓存实现方式,包括Cache接口的基础实现PerpetualCache,以及各种装饰模式如SynchronizedCache、BlockingCache、FifoCache、LruCache等。探讨了每种缓存策略的特点和应用场景,如线程安全、定时清除、最近最少使用和先进先出策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Cache 类主要提供 put/get/remove 方法.
public interface Cache {

/**

  • cache 的 id.
    */
    String getId();

/**

  • @param key Can be any object but usually it is a {@link CacheKey}
  • @param value The result of a select.
    */
    void putObject(Object key, Object value);

/**

  • @param key The key
  • @return The object stored in the cache.
    */
    Object getObject(Object key);

/**

  • As of 3.3.0 this method is only called during a rollback
  • for any previous value that was missing in the cache.
  • This lets any blocking cache to release the lock that
  • may have previously put on the key.
  • A blocking cache puts a lock when a value is null
  • and releases it when the value is back again.
  • This way other threads will wait for the value to be
  • available instead of hitting the database.
  • @param key The key
  • @return Not used
    */
    Object removeObject(Object key);

/**

  • Clears this cache instance.
    */
    void clear();

/**

  • Optional. This method is not called by the core.
  • @return The number of elements stored in the cache (not its capacity).
    */
    int getSize();

/**

  • Optional. As of 3.2.6 this method is no longer called by the core.
  • Any locking needed by the cache must be provided internally by the cache provider.
  • @return A ReadWriteLock
    */
    default ReadWriteLock getReadWriteLock() {
    return null;
    }

}

CacheException 和所有的异常类一样.

PerpetualCache 是对 Cache 接口的基本实现,底层使用的是 HashMap.

CacheKey. 缓存在 Cache 中的 key. 换句话说是存放在 Map 中的 key. 为什么会有这种需求了?因为比如说我要缓存某条 sql 对应的结果. 那么一条sql 是否对应着表明、查询条件等,他们合起来作为一个 key 存放在 map 中.

我们再看下 Cache 的装饰模式的实现.

SynchronizedCache:核心方法都加上了 synchronized 关键字.

BlockingCache:阻塞 Cache. 如何理解了?就是 put 的时候随便 put,没有限制,但是获取的时候就有限制了.
首先它会申请获取锁,然后再获取数据,最后释放锁. 如果再次期间,其他线程也来获取数据,将会被阻塞.
如下方法可能造成死锁把?不会. 我们还需要结合 putObject 来看. 使用的锁来完成的.
线程 A 去获取数据,没有获取到,然后阻塞,然后去查询数据库,获得数据,放入缓存,释放锁,这才是一整套流程.
public Object getObject(Object key) {
acquireLock(key);
Object value = delegate.getObject(key);
if (value != null) {
releaseLock(key);
}
return value;
}

public void putObject(Object key, Object value) {
try {
delegate.putObject(key, value);
} finally {
releaseLock(key);
}
}

FifoCache:先进先出 Cache. 该 Cache 实现中,有一个双向队列来记录 key. 容量为 1024. 一旦超过这个容量,则会把最开始的 CacheKey 移除. 遗憾的是不是线程安全的.

LruCache:最近最久为使用 Cache. 它的底层采用一个 HashMap 来记录访问的顺序. 换句话说,LruCache 功能的实现,在于 HashMap.
public void setSize(final int size) {
keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {
private static final long serialVersionUID = 4267176411845948333L;

  @Override
  protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
    boolean tooBig = size() > size;
    if (tooBig) {
      eldestKey = eldest.getKey();
    }
    return tooBig;
  }
};

}

如下代码就是回收 key. 关于 HashMap 为啥能实现 LRU 的细节,可以参考 HashMap 的源码实现.
private void cycleKeyList(Object key) {
keyMap.put(key, key);
if (eldestKey != null) {
delegate.removeObject(eldestKey);
eldestKey = null;
}
}

ScheduledCache 是一个带定时清除功能的 cache. 默认清理时间是 1 小时.
private final Cache delegate;
protected long clearInterval;
protected long lastClear;

SerializedCache 是一个可以将 value 序列化的 cache. 在 put 的时候自动序列化,在 get 的时候自动的反序列化.

SoftCache 软引用 Cache. 这个比较特殊.
// 最近使用到的会加入其中
private final Deque hardLinksToAvoidGarbageCollection;
// 软引用队列.
private final ReferenceQueue queueOfGarbageCollectedEntries;
// 代理的 Cache.
private final Cache delegate;
// 翻译成强引用队列的数量.
private int numberOfHardLinks;

public void putObject(Object key, Object value) {
// 移除软引用队列中的元素.
removeGarbageCollectedItems();
delegate.putObject(key, new SoftEntry(key, value, queueOfGarbageCollectedEntries));
}

public Object getObject(Object key) {
Object result = null;
@SuppressWarnings(“unchecked”) // assumed delegate cache is totally managed by this cache
SoftReference softReference = (SoftReference) delegate.getObject(key);
if (softReference != null) {
result = softReference.get();
if (result == null) {
delegate.removeObject(key);
} else {
// See #586 (and #335) modifications need more than a read lock
// 最近使用到的数据会加入到强引用队列中.
synchronized (hardLinksToAvoidGarbageCollection) {
hardLinksToAvoidGarbageCollection.addFirst(result);
if (hardLinksToAvoidGarbageCollection.size() > numberOfHardLinks) {
hardLinksToAvoidGarbageCollection.removeLast();
}
}
}
}
return result;
}

TransactionalCache:有事务的那么一点意思,也就是说数据不是直接插入到缓存中,而是先放到一个中间地方,等没问题了在提交 到 cache 中.
// 实际缓存存放地址.
private final Cache delegate;
// 字段为true时,则表示当前TransactionalCache不可查询,且提交事务时,会将底层的Cache清空
private boolean clearOnCommit;
private final Map<Object, Object> entriesToAddOnCommit;
// 觉得这个变量作用不大.
private final Set entriesMissedInCache;

如果 clearOnCommit 为 true,表示不可查询.
public Object getObject(Object key) {
// issue #116
Object object = delegate.getObject(key);
if (object == null) {
entriesMissedInCache.add(key);
}
// issue #146
if (clearOnCommit) {
return null;
} else {
return object;
}
}
// 将数据先放到 entriesToAddOnCommit 集合中.
public void putObject(Object key, Object object) {
entriesToAddOnCommit.put(key, object);
}

commit 核心逻辑.
private void flushPendingEntries() {
for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
delegate.putObject(entry.getKey(), entry.getValue());
}
for (Object entry : entriesMissedInCache) {
if (!entriesToAddOnCommit.containsKey(entry)) {
delegate.putObject(entry, null);
}
}
}

WeakCache 和 SoftCache 实现差不多,不同的是一个是 WeakReference,另一个是 SoftReference.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值