一级缓存全局设计
设计目的
方便的管理一级缓存,当有新的缓存需求出现时,尽可能的少改代码
框架背景
thinkphp6,需要借助模型事件特性
设计要点
- 支持管理两种缓存场景,1、配置型小表整表缓存为单key 2、业务型大表分id缓存为多key
- 缓存与模型绑定,通过模型自动拼接缓存key,如有新的缓存需求,配置下模型类和key就能够实现
- 借助框架模型事件实现缓存的自动清除与刷新
- 缓存时效加上一个随机数防雪崩
- freshModelCache、destroyModelCache方法用于主动预热和刷新缓存
- getDataFromCache、getDataFromCacheMulti是主要的对外提供服务的方法,用于读取数据,未命中缓存则读取数据库
- 所有方法调用从模型类开始,所有模型类都继承了这些方法
具体实现
/**
* 获取模型缓存键名
*
* 根据模型类和ID生成对应的缓存键名,用于缓存管理
*
* @param mixed $id 数据主键
* @return string 缓存键名
*/
public static function getModelCacheKey($id)
{
$model_class = get_called_class();
//以id为key管理缓存,具备一定数据量的业务表
$map_id_key = [
User::class => config('redis_schema.schema.user.info'),
Staff::class => config('redis_schema.schema.user.staff'),
Department::class => config('redis_schema.schema.user.department'),
Company::class => config('redis_schema.schema.user.company'),
Role::class => config('redis_schema.schema.user.role'),
];
//缓存整表,用于配置型小表
$map_all_table = [
BaseLifecycleStatus::class => config('redis_schema.schema.system.base_lifecycle_status'),
];
if(isset($map_id_key[$model_class])){
if($id == 0){
throw new CommonException('非法的ID');
}
return $map_id_key[$model_class].':'.$id;
}
if(isset($map_all_table[$model_class])){
return $map_all_table[$model_class];
}
return '';
}
/**
* 数据写入后的回调方法
*
* 当数据写入完成后,根据模型名称删除相应的缓存信息,实现缓存更新机制。
* 目前只处理User模型的缓存删除。
*
* @param Model $data 写入的数据模型实例
* @return void
*/
public static function onAfterWrite($data)
{
$key = self::getModelCacheKey($data->id);
if(!empty($key)){
Cache::delete($key);
}
}
/**
* 数据删除后的回调方法
*
* 当数据被删除后,根据模型名称删除相应的缓存信息,实现缓存更新机制。
*
* @param Model $data 被删除的数据模型实例
* @return void
*/
public static function onAfterDelete($data)
{
$key = self::getModelCacheKey($data->id);
if(!empty($key)){
Cache::delete($key);
}
}
/**
* 获取数据,优先从缓存获取,缓存不存在则从数据库获取
*
* @param mixed $id 数据主键
* @return array|null
*/
public static function getDataFromCache($id = 0): ?array
{
$key = self::getModelCacheKey($id);
if(empty($key)){
throw new CommonException('当前模型类未纳入缓存管理');
}
// 尝试从缓存获取数据
$data = Cache::get($key);
// 如果缓存中没有数据,则从数据库查询
if (empty($data)) {
if(empty($id)){
$data = self::select();
}else{
$data = self::where('id', $id)->find();
}
if (!empty($data)) {
$data = $data->toArray();
// 将数据存入缓存,设置过期时间
Cache::set($key, $data, getCacheTtl((new SettingRepository())->getSetting(['system_cache_ttl'])));
}
}
return $data;
}
/**
* 批量从缓存中获取数据
*
* @param array $ids 需要获取数据的ID数组
* @return array 以ID为键的数据数组
*/
public static function getDataFromCacheMulti($ids){
$res = [];
foreach ($ids as $id) {
$res[$id] = self::getDataFromCache($id);
}
return $res;
}
/**
* 刷新模型缓存
*
* 从数据库重新加载数据并更新缓存,用于在数据变更后保持缓存与数据库的一致性
*
* @throws CommonException 当前模型类未纳入缓存管理时抛出异常
* @return void
*/
public static function freshModelCache()
{
$key = self::getModelCacheKey(0);
if(empty($key)){
throw new CommonException('当前模型类未纳入缓存管理');
}
Cache::set($key, self::select()->toArray(), getCacheTtl((new SettingRepository())->getSetting(['system_cache_ttl'])));
}
/**
* 删除模型缓存
*
* 删除模型在缓存中的数据,用于在数据变更后清理缓存
*
* @throws CommonException 当前模型类未纳入缓存管理时抛出异常
* @return void
*/
public static function destroyModelCache(){
$key = self::getModelCacheKey(0);
if(empty($key)){
throw new CommonException('当前模型类未纳入缓存管理');
}
Cache::delete($key);
}