缓存主要用来提高查询效率。以计算机的 CPU 为例,CPU 具有三级缓存,性能依次降低,优先从一级缓存查询,一级缓存未命中时再从二级缓存查询,二级缓存未命中时再从三级缓存查询。
MyBatis 缓存抽象
最简单的缓存使用 Map 即可实现,然而由于需要支持不同的使用场景,因此 MyBatis 将缓存抽象出一个 Cache 接口,定义如下:
public interface Cache {
// 获取当前缓存的标识符
String getId();
// 存入缓存对象
void putObject(Object key, Object value);
// 获取缓存对象
Object getObject(Object key);
// 移除缓存对象
Object removeObject(Object key);
// 清空缓存
void clear();
// 获取缓存的对象数量
int getSize();
// 废弃的接口,3.2.6 版本开始不再使用
default ReadWriteLock getReadWriteLock() {
return null;
}
}
可以看到 Cache 主要提供的功能就是添加、移除对象,MyBatis 会根据配置使用不同的实现,各实现具体如下:
缓存类型 | 特点 |
PerpetualCache | 永久存储对象的缓存,使用 Map 实现 |
BlockingCache | 缓存装饰器,使用 CountDownLatch 实现,支持阻塞式获取,获取不到值时当前线程会被阻塞,直到其他线程存入值 |
FifoCache | 缓存装饰器,使用 Deque 实现,最大存储 1024 个对象,超过最大值时使用先进先出的方式移除旧对象 |
LoggingCache | 缓存装饰器,获取对象时会打印日志,对命中率进行简单的统计 |
LruCache | 缓存装饰器,使用 LinkedHashMap 实现,最大存储 1024 个对象,超过最大值时使用最近最少使用的方式移除旧对象 |
ScheduledCache | 缓存装饰器,存储的对象具有一个小时的生命周期,存取或者移除对象时会将过期的对象移除 |
SerializedCache | 缓存装饰器,存放的对象必须实现 Serializable 以支持序列化,存放的是对象序列话后的数组,存取前后会进行序列化和反序列化操作 |
SoftCache | 缓存装饰器,使用 SoftReference 实现,以便内存不够时进行垃圾回收 |
SynchronizedCache | 缓存装饰器,方法前添加 synchronized,每个线程需要获取到锁才能存取对象 |
TransactionalCache | 缓存装饰器,commit 时才把对象刷新到目标 Cache 中 |
WeakCache | 缓存装饰器,使用 WeakReference 实现,每次垃圾回收都会清空缓存对象 |
从上面 Cache 的表格中我们可以看到,MyBatis 使用装饰器模式定义了很多 Cache 的实现,以 LoggingCache 为例,我们看下 MyBatis 对装饰器模式的使用。
public class LoggingCache implements Cache {
// 日志对象
private final Log log;
// 目标 Cache
private final Cache delegate;
// 请求获取对象的数量
protected int requests = 0;
// 命中数量
protected int hits = 0;
public LoggingCache(Cache delegate) {
this.delegate = delegate;
this.log = LogFactory.getLog(getId());
}
@Override
public void putObject(Object key, Object object) {
delegate.putObject(key, object);
}
@Override
public Object getObject(Object key) {
requests++;
final Object value = delegate.getObject(key);
if (value != null) {
hits++;
}
if (log.isDebugEnabled()) {
log.debug("Cache Hit Ratio [" + getId() + "]: " + getHitRatio());
}
return value;
}
// 获取命中率
private double getHitRatio() {
return (double) hits / (double) requests;
}
... 省略部分方法
}
LoggingCache 类对目标 Cache 进行装饰,持有目标 Cache 的引用,当调用 Cache 接口方法时委托给目标 Cache 处理,LoggingCache 类对#getObject方法进行增强,记录了请求次数,命中次数,并且打印了日志。其他装饰器的实现和 LoggingCache 类似。
如何在 MyBatis 中配置缓存