前言
我们在使用 Cache 这个 Facede 的时候,可以知道 laravel 是提供了六种缓存实现方案,这六种配置在 app/cache.php 的 stores 数组配置中可以看到。对于 redis 和 memcached 这种实现方式,相信我们大家都很了解,直接有相关的方法可以实现键值对的添加和取消。但是如果使用文件作为缓存,是怎么实现的过期时间处理呢?
文件缓存实现原理
- 1.存储的数据格式
原数据
\Cache::put('key',['value',1,2,3,'e'],1);
缓存中格式
1561791364a:5:{i:0;s:5:"value";i:1;i:1;i:2;i:2;i:3;i:3;i:4;s:1:"e";}
public function put($key, $value, $minutes)
{
$this->ensureCacheDirectoryExists($path = $this->path($key));
dd($this->expiration($minutes),$this->expiration($minutes).serialize($value));
$this->files->put(
$path, $this->expiration($minutes).serialize($value), true
);
}
1561791599
"1561791599a:5:{i:0;s:5:"value";i:1;i:1;i:2;i:2;i:3;i:3;i:4;s:1:"e";}"
- 实现原理
缓存文件其实是以键为文件名,文件中,过期时间为键,序列化的值为值的值。
在取值的时候,如果发现时间过期,则删除本文件
redis 的清除过期键值缓存策略也包括这种方案
- 缓存文件夹的处理
可以看到文件上还有两级文件夹,但从代码中没有找到删除文件夹的处理。我个人想,可能文件夹是不会删除的
protected function path($key)
{
$parts = array_slice(str_split($hash = sha1($key), 2), 0, 2);
dump($parts,sha1($key));
dd($this->directory.'/'.implode('/', $parts).'/'.$hash);
return $this->directory.'/'.implode('/', $parts).'/'.$hash;
}
图片.png
缓存方式
我们首先可以查看 \Cache::get('key')
这个函数的文件处理是怎么实现的
代码追踪
1.\Illuminate\Cache\Repository::get($key, $default);
public function get($key, $default = null)
{
if (is_array($key)) {
return $this->many($key);
}
$value = $this->store->get($this->itemKey($key));
if (is_null($value)) {
$this->event(new CacheMissed($key));
$value = value($default);
} else {
$this->event(new CacheHit($key, $value));
}
return $value;
}
2.此处的 $this->store 代表的就是文件缓存类 \Illuminate\Cache\FileStore
public function get($key)
{
return $this->getPayload($key)['data'] ?? null;
}
protected function getPayload($key)
{
// 1.获取文件路径
$path = $this->path($key);
// 2.获取文件中的全部内容
try {
$expire = substr(
$contents = $this->files->get($path, true), 0, 10
);
} catch (Exception $e) {
return $this->emptyPayload();
}
// 3.查看内容的时间
if ($this->currentTime() >= $expire) {
// 4.如果时间过期,则删除此文件
$this->forget($key);
return $this->emptyPayload();
}
$data = unserialize(substr($contents, 10));
$time = ($expire - $this->currentTime()) / 60;
return compact('data', 'time');
}
// $key 对应的文件 文件名是做过加密的,但是 $key 值对应的文件名是不变的,大家不用关系这个函数
protected function path($key)
{
$parts = array_slice(str_split($hash = sha1($key), 2), 0, 2);
return $this->directory.'/'.implode('/', $parts).'/'.$hash;
}
apc 缓存
这个缓存的解释我不是很清楚,可能理解可能是减少了编译时间。他的使用场景是比较小的数据。有资料表示比 redis 还快。但是不要存储数据量比较大的数据,因为他的空间很有限
自己写的缓存类
<?php
namespace Core;
/**
* Class Cache
* @package Core缓存
*/
class Cache
{
/**
* 存入数据
* @param $key
* @param $data
* @param int $cacheTime
* @return bool
*/
public static function put($key, $data, $cacheTime = 60)
{
$file = BASE_PATH . '/storage/cache/' . $key;
touch($file);
return file_put_contents($file, time() + $cacheTime . ':' . serialize($data));
}
/**
* 获取缓存数据
* @param $key
* @return string|array|null
*/
public static function get($key)
{
$file = BASE_PATH . '/storage/cache/' . $key;
if (!file_exists($file)) {
// 缓存的键不存在
return null;
}
$data = file_get_contents($file);
if (!$data) {
// 数据为空
return null;
}
$startTime = substr($data, 0, 10);// 数据写入时间
if ($startTime <= time()) {
// 文件过期了
unlink($file);
return null;
}
// 获取数据,并解析序列化 时间戳是10位数字,:占一位,所以序列化的数据是从第11位开始的
return unserialize(substr($data, 11));
}
}
网友评论