mybatis源码解读:cache包(缓存基本功能)

1.简述:

Mybatis每秒可能要处理数万条数据库查询请求,而这些请求可能是重复的。缓存能够减低数据库查询次数,提升mybatis的性能。

mybatis缓存使得每次数据库查询请求都会优先经过缓存系统的过滤,只有没有命中缓存的情况下才会去查询数据库。

cache包就是mybatis缓存能力的提供者。

2.cache包结构与cache包接口

cache包是典型的装饰器模式应用案例,在impl包存放实现类,在decorators包中存放了众多的装饰器类。而cache接口是实现类和装饰器类的共同接口。

cache接口定义了实现类和装饰器类必须实现的代码,源代码如下:


public interface Cache {

/**
* 获取缓存id
* @return 缓存id
*/
String getId();


/**
* 向缓存写入一条信息
* @param key 信息的键
* @param value 信息的值
*/
void putObject(Object key, Object value);

/**
* 从缓存中读取一条信息
* @param key 信息的键
* @return 信息的值
*/
Object getObject(Object key);


/**
* 从缓存中删除一条信息
* @param key 信息的键
* @return 原来的信息值
*/
Object removeObject(Object key);


/**
* 清空缓存
*/
void clear();

/**
* 读取缓存中信息的数目
* @return 信息的数目
*/
int getSize();


/**
* 获取读写锁,该方法已经废弃
* @return 读写锁
*/
default ReadWriteLock getReadWriteLock() {
return null;
}

}

3.缓存键

3.1 原理:

mybatis每秒过滤众多的数据库查询操作,所以对缓存键要求有:

1.键唯一性

2.高效比较

3.高效生成

所以mybatis设计了CacheKey类作为缓存键,主要属性如下:


// 乘数,用来计算hashcode时使用
private final int multiplier;
// 哈希值,整个CacheKey的哈希值。如果两个CacheKey该值不同,则两个CacheKey一定不同
private int hashcode;
// 求和校验值,整个CacheKey的求和校验值。如果两个CacheKey该值不同,则两个CacheKey一定不同
private long checksum;
// 更新次数,整个CacheKey的更新次数
private int count;
// 更新历史
private List<Object> updateList;
/**
* 更新CacheKey
* @param object 此次更新的参数
*/
public void update(Object object) {
int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object);

count++;
checksum += baseHashCode;
baseHashCode *= count;

hashcode = multiplier * hashcode + baseHashCode;

updateList.add(object);
}

可见每一次update操作都会引发count、checksum、 hashcode值的变化,并把更新值放进updateList.

/**
* 比较当前对象和入参对象(通常也是CacheKey对象)是否相等
* @param object 入参对象
* @return 是否相等
*/
@Override
public boolean equals(Object object) {
// 如果地址一样,是一个对象,肯定相等
if (this == object) {
return true;
}
// 如果入参不是CacheKey对象,肯定不相等
if (!(object instanceof CacheKey)) {
return false;
}
final CacheKey cacheKey = (CacheKey) object;
// 依次通过hashcode、checksum、count判断。必须完全一致才相等
if (hashcode != cacheKey.hashcode) {
return false;
}
if (checksum != cacheKey.checksum) {
return false;
}
if (count != cacheKey.count) {
return false;
}

// 详细比较变更历史中的每次变更
for (int i = 0; i < updateList.size(); i++) {
Object thisObject = updateList.get(i);
Object thatObject = cacheKey.updateList.get(i);
if (!ArrayUtil.equals(thisObject, thatObject)) {
return false;
}
}
return true;
}

在比较CacheKey对象是否相等时,会先进行类型判断,然后依次通过hashcode、checksum、count判断,必须完全一致才相等,以上操作都比较简单,能在很短的时间内完成。如果2个对象各项属性完全一致则会详细比较2个对象的变动历史,这一步虽然很复杂,但可以保证绝对不会出现碰撞问题。

这种设计好处:在准确度和效率之间取得平衡。