abei1982 2017-03-27 23:24:16 4548次浏览 4条回复 4 1 0

有的兄弟一定会说,File缓存有什么好讲的那?不就是一个set,然后在一个get么?那你知道的只是皮毛~

的确如大家所说,缓存从表现层看就是一个set(放数据),然后再一个get(拿数据),但是请记住,这样是不够的,如果你不了解每个缓存的实现原理、存储特点等等,你是无法使用对场景的,什么时候用File缓存、什么时候用DB缓存、什么时候用Memcache缓存,这些才是我们要学的。

依然先写目录

  1. FileCache的定义
  2. FileCache的基本用法
  3. FileCache存储内容时的逻辑实现

FileCache的定义

可能一句话就可以说明白,将某些数据存到文件中,下次读取的时候直接从文件中读,分流压力。

基本用法

为了统一接口,yii2将所有类型的缓存都统一成以下这些方法。

get() // 根据一个指定的key获取缓存值,如果缓存不存在或已经过期则返回false
set() // 直接保存一个值到缓存(不考虑是否存在,直接替换)
add() // 如果此刻缓存不存在则保存,否则忽略
multiGet() // 一次获取多个key的缓存值,参数为一个key的数组["key1","key2"],返回值也是一个数组["key1"=>"valuw1","key2"=>"value2"]
multiSet() // 一次存储多个缓存
mulitAdd() // 一次添加多个缓存
delete() // 删除某个缓存
flush() // 删除所有缓存

以上8个方法我想通过文档大家都可以看明白,着实没什么难度,还有两个方法在这里重点说下,它们理解起来稍微蹩脚一些。

exists()
getOrSet()

exists($key) 顾名思义,检查一个缓存是否存在(缓存过期 = 过期)。对于这个函数要注意以下几点

  1. 当缓存的数据量很大时,使用exists要比get快,但是如果缓存没有原生的exists,则exists会去模拟一下,这种情况下exists不会比get有性能提高,FileCache 的exists未有提升。
  2. exists并不会检查缓存依赖,所以如果一个缓存有依赖而依赖的变化导致我们通过get获取的值是false,但是有可能exists获取的是true,这点要注意下。

getOrSet($key, Closure $closure, $duration = null, $dependency = null) 这是v2.0.11增加的一个新的方法。

$data = $cache->getOrSet($key, function () {
    return $this->calculateSomething();
});

等同于

$data = $cache->get($key);
if ($data === false) {
    $data = $this->calculateSomething();
    $cache->set($key, $data);
}

这个函数的用意是简单的-如果有则获取,没有则建立,但是其中的$closure要提一下,它接收一个闭包。闭包是php5.3引入的一个新概念,如果不知道的可以用下面网址补课一下 http://www.cnblogs.com/melonblog/archive/2013/05/01/3052611.html

于是我们就可以写一个你可能不常见的缓存代码

$cache->getOrSet(['user','id'=>$id],function($cache) use ($id){
	return User::findOne($id);
});

缓存每一个会员的信息,使用use,最少代码量。

以上就是yii2 Cache对支持的所有缓存统一的10个方法,下面我们会针对FileCache进行一些原理性说明,为了方便大家阅读,我们用问答模式。

重点要说的

上帝问: 北哥,看文档我看什么get、set、getOrSet的key除了一个字符串,还能接收一个数据结构,是什么意思?

北哥答:是的,你说的不错,比如我们现在要缓存每个文章,我们可以使用以下两个方法

// key是string的
$cache->set("article-1",$data);
// key是结构体,比如数组
$cache->set(["article","id"=>1],$data);

这两种形式都可以,我想你一定会问这些key是如何识别的那,在runtime的 cache我如何找到他们那?

上帝问: 是的北哥,您说的对,比如我的key=abc,那么存储的时候是如何根据key来建立文件夹或缓存文件的那,他们是如何命名的那?

北哥答: yii对于文件缓存key的处理如下,我用伪代码回答你。

//	对用户缓存定义的key进行再加工
if($key == 字符串){
	if($key字符串只含有数字或字母 && $key的长度 <= 32 ){
		return $key;
	}else{
		md5($key)
	}
}else{
	return md5(json_encode($key)); // 数字的key、数组的key都被md5了。
}

因此你能看到可能让你蒙圈的缓存文件,如下图

14906240386775.png

现在不蒙圈了吧。但是一定要注意,看下面代码

$cache->set("123",$data);
$cache->set(123,$data);

语句1会生成一个123.bin的缓存文件,因为"123"是字符串,而语句2的123是数字,会生成md5(123)后的.bin文件。

现在你明白了文件命名规则,我想上帝你一定也被那些13、59、a3、le这样的文件夹弄晕了吧,再仔细看一下,是的,你发现了。。。

是文件名的前两个字母

恭喜你,这是一个多么准确且伟大的发现。但是,我想你也一定见过这样的缓存目录

14906244579891.png

目录不止一级,很多级,这样是有道理的,可以保证一个文件夹内文件不会过多,但是这个是如何实现的那?

这仅仅是一个变量决定,它叫directoryLevel(默认为1)

$cache->directoryLevel = 3;

当然你也可以在web的cache里配置,还是按照你刚才发现的密码,如果directoryLevel=3,则把文件名从头开始,每2字符个做一个文件夹(如果文件名只有3个字符,则建立到第二个的时候停止就好了)。

好的上帝,现在你明白了吧~

上帝问: 你说的我明白了,我还有个问题,比如我在我模块里set了一个key=abc的缓存,别人也set了一个key=abc的缓存(和我的key不是一个含义),那岂不是被覆盖了,能不能避免?

北哥答: 必须的了,你可以使用一个叫做keyPrefix的变量,它为你的缓存文件名字加一个前缀,看代码

$cache->keyPrefix = "nai8_";
$cache->set("hello","hello keyPrefix");

结果如下

14906250288438.png

这回你不怕了,看看谁还敢和你冲突。

上帝问: 我基本都明白了,再问你一个问题,回答完后我就回花果山了,就是比如我写了$cache->set("a","abc",1000),但是当我get的时候,yii怎么知道到了1000秒,已经过期还是没过期那,我看文件内容里没有1000这个数字啊,到底存在什么地方?

北哥答: 你说的没错,这要介绍一个php的函数了 --- touch(),这个函数可以修改一个文件的上一次修改时间,当我们执行 $cache->set("a","abc",1000)时候,yii的FileCache做了一件事情,看代码

touch("缓存文件",(time() + 1000));

你明白了吧,当我们读的时候,只需要判断filetime("缓存文件")的上次修改时间是否大于现在time(),不就可以了么,多简单的事情。

OMG~ Fly fly fly ......

可是我还没说完~

下面开始北哥自言自语一小段,讲讲缓存依赖(set、add、getOrSet都有这个参数),什么意思那,简单的说就是一个缓存和某个东东有关联,当这个东东发生变化时,则这个缓存失效(既是当前过期时间还没到)

因为篇幅问题,我们这里先说一个Cache的数据库依赖,以后会在“北哥大话Yii2缓存机制”单独拿出一篇来讲各种依赖。

$dependency = new \yii\caching\DbDependency(
    ['sql' => 'SELECT count(*) FROM user']
);
// 当数据库字段发生变化时,该缓存失效
$cache->add('three', 'hello world!', 3000, $dependency);

依赖还是蛮有用的,比如一些统计数据的显示做缓存,当数据变化时缓存数据能得到及时的更新。

讲的好长~作为“北哥大话Yii2缓存机制”第一篇,希望大家喜欢,为了一段日子里我会专注于分享yii2的缓存机制。

感谢兄弟连各位兄弟的大力支持。

原文链接 http://nai8.me/sapper-index.html

下面是我的小广告,关注公众号,分享干货

觉得很赞
您需要登录后才可以回复。登录 | 立即注册