Laravel缓存优化:保留旧缓存直至新缓存生成的实现方案
Laravel 文件缓存实现新旧缓存无缝切换的方案
一、原生推荐方案:版本化缓存键
不用清理整个缓存池,给目标缓存键加上版本标识,生成新缓存时使用新版本键,完成后再切换当前生效的版本号,全程保留旧缓存直到新缓存完全就绪。
代码示例
生成新缓存
// 生成唯一版本号(用时间戳或自增ID都可以) $newVersion = time(); // 将新数据存入带版本号的缓存键 Cache::forever(static::DATA_STRUCTURE . '_v' . $newVersion, json_encode($tree)); // 更新当前生效的版本号 Cache::forever('DATA_STRUCTURE_CURRENT_VERSION', $newVersion); // 可选:异步清理旧版本缓存(避免阻塞主流程) $oldVersion = Cache::get('DATA_STRUCTURE_CURRENT_VERSION', null); if ($oldVersion && $oldVersion !== $newVersion) { Cache::forget(static::DATA_STRUCTURE . '_v' . $oldVersion); }
读取缓存
// 获取当前生效的版本号 $currentVersion = Cache::get('DATA_STRUCTURE_CURRENT_VERSION', 0); $targetCacheKey = static::DATA_STRUCTURE . '_v' . $currentVersion; $treeData = Cache::get($targetCacheKey); // 兜底逻辑:如果缓存不存在(首次部署或版本切换异常),重新生成 if (!$treeData) { $tree = // 从数据库拉取数据结构的业务逻辑 Cache::forever($targetCacheKey, json_encode($tree)); Cache::forever('DATA_STRUCTURE_CURRENT_VERSION', $currentVersion); }
这个方案完全基于Laravel原生Cache组件实现,无需修改缓存驱动,还能避免cache:clear清空所有缓存的副作用,只针对目标缓存做版本管理,逻辑简单可靠。
二、目录替换思路的优化实现
如果坚持用目录替换的方式,可以临时切换文件缓存的存储路径,生成新缓存后再替换原目录:
use Illuminate\Support\Facades\File; // 临时指定新缓存目录 config(['cache.stores.file.path' => storage_path('framework/cache/data_new')]); // 生成新缓存到临时目录 Cache::store('file')->forever(static::DATA_STRUCTURE, json_encode($tree)); // 替换旧缓存目录 $oldCachePath = storage_path('framework/cache/data'); $newCachePath = storage_path('framework/cache/data_new'); // 先删除旧目录,再重命名新目录(注意:替换瞬间可能有请求读不到缓存) File::deleteDirectory($oldCachePath); File::move($newCachePath, $oldCachePath);
注意事项
- 替换目录的瞬间,若有请求正在读取缓存,可能出现短暂的缓存缺失
- 需确保Laravel进程对缓存目录有读写权限
- 该方案会影响所有文件缓存的键,不像版本化方案只针对目标缓存
三、耗时缓存的异步生成方案
如果缓存生成逻辑耗时较长,可以把生成逻辑放到Laravel队列中异步执行,后台完成后再切换版本,完全不阻塞前端请求:
// 触发异步缓存生成任务 GenerateDataStructureCache::dispatch(); // 队列任务类的handle方法 public function handle() { $newVersion = time(); $tree = // 从数据库拉取数据结构的业务逻辑 Cache::forever(static::DATA_STRUCTURE . '_v' . $newVersion, json_encode($tree)); // 先记录旧版本,再切换新版本 $oldVersion = Cache::get('DATA_STRUCTURE_CURRENT_VERSION', null); Cache::forever('DATA_STRUCTURE_CURRENT_VERSION', $newVersion); // 延迟清理旧缓存,避免刚切换就有请求还在读旧缓存 if ($oldVersion) { Cache::put('DATA_STRUCTURE_OLD_VERSION', $oldVersion, now()->addHour()); // 可以再开一个延迟任务清理旧缓存 CleanOldDataStructureCache::dispatch($oldVersion)->delay(now()->addHour()); } } // 清理旧缓存的延迟任务 public function handle($oldVersion) { Cache::forget(static::DATA_STRUCTURE . '_v' . $oldVersion); Cache::forget('DATA_STRUCTURE_OLD_VERSION'); }
内容的提问来源于stack exchange,提问作者Denissio Inkognito
相关产品推荐
相关产品推荐

