缓存
缓存分层
EasyRelation 中共设计有两级缓存,参考了 Mybatis 中的设计,在进行数据关联时,会依次经过 一级缓存 --> 二级缓存 ---> 数据提供源
,从而提高数据获取的效率。
这里简单了解一下一级缓存与二级缓存:
- 一级缓存:单次数据关联操作内的缓存,缓存的数据只在这个关联过程内有效,一级缓存根据一定规则,会自动开启。
这里的单次数据关联操作指的是调用一次
injectRelation
方法内的执行流程。
- 二级缓存:全局缓存,比如使用 Redis 作为缓存,二级缓存需要手动开启。
一级缓存
为什么需要一级缓存
在进行数据关联时,在对一个集合进行关联,或者一个对象内有平铺关联的操作时, 针对同一个数据提供者,且条件相同时,每一次关联都查询一次数据库,或者调用一次 Rpc 接口,会白白浪费系统的资源,并且还会导致类似于数据库中的脏读、幻读、不可重复读等问题。
为了解决这个问题,EasyRelation 会在执行时,进行判断是否需要一级缓存,当需要时,会自动打开缓存,缓存条件和相应的结果,从而保证性能和同一次关联操作中的一致性问题。
一级缓存实现
当一级缓存开启状态时,会先到缓存中查询是否有,如果没有,则经过二级缓存、数据提供者,获取数据,最终存储到一级缓存中。
一级缓存是在当前线程的 ThreadLocal
中绑定了一个 Map 对象,执行前根据一定规则开启,执行后,会自动清除。
一级缓存的开启条件
为了进一步节省性能,一级缓存并不会在每一次关联操作中开启,只会在判断需要的时候才会开启。
目前当下面两种情况时,会自动开启:
- 当一次对一个集合中的元素进行关联时;
- 当在一个对象中出现平铺关联的操作时;
即,EasyRelation 会判断不会对同一个数据提供源进行多次调用时,则不会开启一级缓存。
二级缓存
二级缓存主要是为了全局缓存,当没有命中一级缓存时,首先会到二级缓存中查找,如果找到相应的值则直接解析返回。
二级缓存的实现
框架中提供了一个二级缓存的实现,实现比较简单,因为一般项目中也会用 Redis 来实现缓存。
针对二级缓存定义了一个接口 —— RelationCache
,接口定义如下:
public interface RelationCache {
/**
* 指定键值是否存在
*
* @param key 键
* @return 指定键值是否存在
*/
boolean hasKey(String key);
/**
* 保存缓存
*
* @param key 键值
* @param value 实际值
* @param time 过期时间,单位:ms
*/
void set(String key, Object value, long time);
/**
* 获取缓存数据
*
* @param key 键值
* @return {@link Object} key对应的值
*/
Object get(String key);
}
默认实现
框架中的默认实现,是基于两个 Map
和 Tasker
来实现的。
保存一个 KV 时,会同时保存其过期时间,通过 Tasker
来过期 key。
集成 Redis 作为 Key
EasyRelation 可以提供了 Redis 缓存的插件包,其中定义了 JDK、Jackson 两种 value 序列化方式。
具体使用可以参考Redis 缓存
自定义缓存
当要实现一个二级缓存时,需要实现该接口,并且声明 InjectRelation
对象时,传入该实现类。
如果使用 SpringBoot,将自定义缓存的实现类声明为一个 Spring 的 Bean 即可。
二级缓存的开启
二级缓存默认是禁用状态,需要手动开启。
首先,在配置 DataProvider
时,可以通过 useCache
配置当前数据提供者的默认缓存开关配置,当前配置只有在配置 @Relation
时, cacheStrategy
为 DEFAULT
时才会生效,详情可以参考配置关联关系#启用缓存
同样还支持每个关联类型内部的个性化配置,比如通过 @Relation
中的 cacheStrategy
配置强制开启缓存或强制关闭缓存。
当通过该属性配置强制开启/关闭时,会忽略 DataProvider
中的缓存开关配置。详情可以参考配置关联关系#启用缓存
缓存 Key 的生成规则
数据提供者唯一标识
+ -
+ 参数列表(参数名称
+ -
+ 参数值
)。
例如:
getUserByUsername-username-admin