实际工作中,提高web应用的性能也是一件非常重要的事情。可能有些人要反驳我,这些优化工作我们面试中用的比较多,实话哈。
提到性能优化,可能会包含很多种操作,比如常见的环境优化如开启Opcache缓存、页面优化如压缩资源文件等等很多。但是一个web应用的性能瓶颈往往就与数据库相挂钩。为了优化数据库查询,我们常常避免一些复杂的查询、增加索引等操作。
但是,以上都不是我们本文的重点。
假设我们的数据量足够大,执行一条sql查询并获取数据的过程就会非常耗时,当然你可以尽可能的从数据库层面去优化,本文我们来看下如何利用数据缓存有效的减少查询次数。
yii提供了一套缓存组件以支持数据缓存,常见的缓存介质都有文件、数据库和内存。那我们用不用针对每一种缓存介质都单独学习一套操作方法呢?这是yii很友好的地方,假设我们现在要把文件缓存换成内存缓存,只需要简单的修改下缓存存储介质的配置即可,代码中对缓存的操作方法改都不用改。下面我们以文件缓存为例。
配置
为了使用缓存,第一步我们需要配置缓存,假设你的多个应用都采用统一介质存储比如文件缓存,只需要在common\config\main.php文件内配置即可1
2
3
4
5'components' => [
'cache' => [
'class' => 'yii\caching\FileCache',
],
],
默认的缓存数据被存储在各个应用的 runtime/cache 目录,我们可以通过 FileCache的cachePath进行修改。
使用
既然缓存以组件的方式配置,我们自然就可以通过 Yii::$app->cache 的方式进行调用。
典型的缓存使用方式可以是下面这样的1
2
3
4
5
6
7
8
9
10
11
12
13
14
15$cache = Yii::$app->cache;
// 尝试从缓存中取回 $data
$data = $cache->get($key);
if ($data === false) {
// $data 在缓存中没有找到,则重新获取这个值
// $data = 数据库获取并处理
// 将 $data 存放到缓存供下次使用
$cache->set($key, $data);
}
// 这儿 $data 可以使用了
我们简单的分析下:
首先程序会通过Yii::$app->cache->get方法获取数据,注意这里传递的参数应保持唯一,如果有多个数据被同一个key使用,那结果可能就是乱七八糟的。key的形式支持数组和字符串,对于一些复杂的业务,比如文章信息,我们可以指定key=[‘article’, $id]
通过如果没获取到有效的数据,get方法会返回false,我们知道php是弱类型语言,所以 0 == false, ‘’ == false等等这些都成立,为了no zuo,这里必须使用===进行判断,如果我们要存入的数据本身就是false怎么办呢?这个建议用把false填充到数组单元中
如果没获取到有效的数据,这个时候我们可以再通过数据库查询获取,然后再通过程序处理之后通过Yii::$app->cache->set方法存储
有同学要说了,这so easy呀,你这文章我看都没有看下去的必要了。别急,我们继续看后面的重点。
对于每一个数据缓存的操作我们都这么写是不是觉得很麻烦,每次都有判断,然后写那么多代码?
在最新的2.0.11版本中,yii封装了一个getOrSet方法,该方法等同上述代码的封装,我们看下getOrSet的源码便知
我们看到第二个参数传递的是一个闭包(不了解php闭包概念的自行google),所以我们可以这样使用1
2
3
4$data = $cache->getOrSet($key, function () use ($outVariable) {
// $data = 数据库获取并处理
return $data;
});
至于后面两个参数,$duration是缓存的有效期,以秒为单位,$dependency是缓存依赖,我们挨个介绍。
因为getOrSet是2.0.11新增的方法,后面我们依然主要介绍第一种非getOrSet方法的使用,其实二者就是一种。
缓存的有效期,即set的第三个参数(从getOrSet的方法中也可以看出),注意哦,这里的有效期是从程序执行的时候开始,填写一个整数即可,比如我有一个数据要缓存1天,那就是1
$cache->set($key, $data, 24*3600);
而不可以写成1
$cache->set($key, $data, time() + 24*3600);
写成这样真的是要到猴年马月才过期了。 =-=!
默认该参数的值是0,即没有有效期,除非有新的缓存覆盖了之前的数据或者人为删除等操作。
我们着重看下最后一个参数,缓存依赖。
依赖,应该算是我们第二次接触这个概念了,之前在资源管理中有介绍AssetBundle的依赖关系。
这里的依赖是什么意思呢?我举一个例子:
假设有多个数据缓存项都缓存了某个用户的相关的数据,比如用户的基本信息,用户的统计信息,当用户真实的数据变化了之后呢,如何保证缓存的用户基本信息和统计信息都是最新的呢?找到所有的缓存项,一个一个的更新?那以后如果我们还需要再缓存用户的其他数据呢?
这个时候我们可以在使用set操作时,为该用户的所有缓存项指定某种依赖,比如用户的id,当用户的信息被更新的时候,可以让所有依赖这个用户id的缓存项都失效,这便是set的最后一个参数缓存依赖的含义。
我们再用代码来诠释一下
数据缓存的使用1
2
3
4use yii\caching\TagDependency;
Yii::$app->cache->set('user_10_profile', $profile, 0, new TagDependency(['tags' => 'user-10']));
Yii::$app->cache->set('user_10_stats', $stats, 0, new TagDependency(['tags' => 'user-10']));
想让缓存失效,只需要调用缓存介质的invalidate方法即可1
\yii\caching\TagDependency::invalidate(Yii::$app->cache, 'user-10');
除了TagDependency,还有下面这些你也可以使用
yii\caching\ChainedDependency:如果依赖链上任何一个依赖产生变化,则缓存失效
yii\caching\DbDependency:如果指定 SQL 语句的查询结果发生了变化,则缓存失效
yii\caching\ExpressionDependency:如果指定的 PHP 表达式执行结果发生变化,则缓存失效
yii\caching\FileDependency:如果文件的最后修改时间发生变化,则缓存失效
最后,送上一些建议,如果你是用的是内存比如memcache、redis缓存,最好缓存一些不那么占用内存的小且频繁使用的数据,内存昂贵,且用且珍惜。对于一些频繁操作的数据,用文件缓存你造不造磁盘IO也很慢啊啊啊~