Laravel支持多种形式的缓存Cache
,从Laravel的源码可看出,缓存的过期判断逻辑是在读取缓存前执行,即如果不是主动删除forget
一个缓存的话,一个缓存生成并过期后,会在下一次读取get
这个缓存时被Laravel删除。
问题来了,如果一个缓存在过期后没有再被读取过,那这个缓存是不是就一直存在?
答案是:完全正确。
倘若是redis做的Laravel缓存,redis服务会定期检查已经过期的缓存并主动清除,然而文件型缓存就没那么幸运,过期后没有再次访问的缓存会一直存在于磁盘中。Laravel的缓存类也没有提供可以主动清除所有过期缓存的方法,要实现这个功能还得自己来。
分析Laravel的缓存服务,缓存数据文件存放在Laravel框架的storage/framework/cache/data
目录,当一个缓存写入put
时,Laravel会将缓存$key做sha1,然后取返回字符串的0~1位和2~3位分别作为data目录下的一级目录名和二级目录名创建目录,最后在二级目录内以sha1完整字符串作为文件名来保存该缓存。缓存文件内容前10个字符组成的字符串为缓存过期时间的时间戳。对比该时间戳信息,即可知道该缓存是否已经过期。
举个例子:
写入一个文件缓存:
Cache::put('myname' , 'superman', 60);
Laravel把key值'myname'做sha1,得到字符串
d13b93ea42804188d277c20f7d6e5be2732148b8
截取字符串0~1位d1
,截取字符串2~3位3b
,并在data目录下创建两层子目录,最终该缓存文件完整的保存路径是
storage/framework/cache/data/d1/3b/d13b93ea42804188d277c20f7d6e5be2732148b8
缓存文件内容:
1586959560s:8:"superman"
其中前10位1586959560就是该缓存的过期时间。了解这个规则,我们只需对缓存data目录下遍历2层目录得到全部缓存文件,逐个打开判断时间戳值,如果时间戳已经过期就把该文件删掉即可。
上php:
$now = time();
$cacheFilesCount = 0;
$cacheFilesExpireCount = 0;
//缓存数据目录绝对路径,根据自己业务目录修改
$cachePath = '/path/to/your/laravel/project/storage/framework/cache/data';
// 第一层目录
$dirsLv1 = getDirs($cachePath);
foreach($dirsLv1 as $dir1) {
// 第二层目录
$dirsLv2 = getDirs($dir1);
foreach($dirsLv2 as $dir2) {
$cacheFiles = scandir($dir2);
foreach($cacheFiles as $cacheFile) {
// 构造缓存文件绝对路径
$cacheFileFullname = $dir2 . '/' . $cacheFile;
if(is_file($cacheFileFullname)) {
// 读取文件
$data = file_get_contents($cacheFileFullname);
$cacheFilesCount++;
if ($data) {
// 获取缓存内过期时间
$startTime = substr($data, 0, 10) * 1;
if ($startTime <= $now) {
// 缓存文件过期了,删掉
unlink($cacheFileFullname);
$cacheFilesExpireCount++;
}
}
}
}
}
}
echo 'cache files count : '. $cacheFilesCount .' , expired count :' . $cacheFilesExpireCount . ' .';
//返回指定目录下的子目录数组
function getDirs($path) {
$subDirFullPaths = [];
$dirs = scandir($path);
foreach($dirs as $subDir) {
if($subDir != '..' && $subDir != '.') {
$dirFullpath = $path . '/' . $subDir;
if(is_dir($dirFullpath)) {
$subDirFullPaths[] = $dirFullpath;
}
}
}
return $subDirFullPaths;
}
把php文件放到Linux内跑定时任务crontab
定时删除过期缓存,如果你单个缓存保存的数据量不大,磁盘空间不吃紧,可以不用跑那么频密,毕竟跑一次要做大量的磁盘IO,并且应尽量在业务低峰时间段执行。