使用缓存依赖和chains

Yii支持需要缓存后端,但是使Yii缓存灵活的是依赖和依赖chaining支持。有一些情况,你不能简单的只缓存1个小时的数据,因为信息随时可能会边。

在这个小节中,我们将会学习如何缓存整个页面,并能在有更新时获取最新的数据。这个页面是一个仪表盘类型的,将会展示5个最新添加的文章,以及总数。

注意:注意一个操作不能被编辑 as it is added,但是一个文章可以。

准备

按照官方指南http://www.yiiframework.com/doc-2.0/guide-start-installation.html的描述,使用Composer包管理器创建一个新的yii2-app-basic应用。

  1. config/web.php中激活缓存组件:
  1. return [
  2. // ...
  3. 'components' => [
  4. 'cache' => ['class' => 'yii\caching\FileCache',
  5. ],
  6. ],
  7. ];
  1. 设置一个新的数据库,并将它配置到config/db.php中:
  2. 运行如下migration:
  1. <?php
  2. use yii\db\Schema;
  3. use yii\db\Migration;
  4. class m160308_093233_create_example_tables extends Migration
  5. {
  6. public function up()
  7. {
  8. $tableOptions = null;
  9. if ($this->db->driverName === 'mysql') {
  10. $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE=InnoDB';
  11. }
  12. $this->createTable('{{%account}}', [
  13. 'id' => Schema::TYPE_PK,
  14. 'amount' => Schema::TYPE_DECIMAL . '(10,2) NOT NULL',
  15. ], $tableOptions);
  16. $this->createTable('{{%article}}', [
  17. 'id' => Schema::TYPE_PK,
  18. 'title' => Schema::TYPE_STRING . ' NOT NULL',
  19. 'text' => Schema::TYPE_TEXT . ' NOT NULL',
  20. ], $tableOptions);
  21. }
  22. public function down()
  23. {
  24. $this->dropTable('{{%article}}');
  25. $this->dropTable('{{%account}}');
  26. }
  27. }
  1. 使用Yii为account和article表生成模型。
  2. 创建protected/controllers/DashboardController.php
  1. <?php
  2. namespace app\controllers;
  3. use app\models\Account;
  4. use app\models\Article;
  5. use yii\web\Controller;
  6. class DashboardController extends Controller
  7. {
  8. public function actionIndex()
  9. {
  10. $total = Account::find()->sum('amount');
  11. $articles = Article::find()->orderBy('id DESC')->limit(5)->all();
  12. return $this->render('index', array(
  13. 'total' => $total,
  14. 'articles' => $articles,
  15. ));
  16. }
  17. public function actionRandomOperation()
  18. {
  19. $rec = new Account();
  20. $rec->amount = rand(-1000, 1000);
  21. $rec->save();
  22. echo 'OK';
  23. }
  24. public function actionRandomArticle()
  25. {
  26. $n = rand(0, 1000);
  27. $article = new Article();
  28. $article->title = "Title #".$n;
  29. $article->text = "Text #".$n;
  30. $article->save();
  31. echo 'OK';
  32. }
  33. }
  1. 创建views/dashboard/index.php
  1. <?php
  2. use yii\helpers\Html;
  3. /* @var $this yii\web\View */
  4. /* @var $total int */
  5. /* @var $articles app\models\Article[] */
  6. ?>
  7. <h1>Total: <?= $total ?></h1>
  8. <h2>5 latest articles:</h2>
  9. <?php foreach($articles as $article): ?>
  10. <h3><?= Html::encode($article->title) ?></h3>
  11. <div><?= Html::encode($article->text) ?></div>
  12. <?php endforeach ?>
  1. 运行dashboard/random-operationdashboard/random-article几次,然后,运行dashboard/index你将会看到如下所示的截图:

使用缓存依赖和chains - 图1

  1. 在页面的底部,点击调试面板上数据库查询的数量:

使用缓存依赖和chains - 图2

看到一个查询列表:

使用缓存依赖和chains - 图3

如何做…

执行如下步骤:

  1. 我们需要修改控制器的代码:
  1. <?php
  2. namespace app\controllers;
  3. use app\models\Account;
  4. use app\models\Article;
  5. use yii\caching\DbDependency;
  6. use yii\caching\TagDependency;
  7. use yii\web\Controller;
  8. class DashboardController extends Controller
  9. {
  10. public function behaviors()
  11. {
  12. return [
  13. 'pageCache' => [
  14. 'class' => 'yii\filters\PageCache',
  15. 'only' => ['index'],
  16. 'duration' => 24 * 3600 * 365, // 1 year
  17. 'dependency' => [
  18. 'class' => 'yii\caching\ChainedDependency',
  19. 'dependencies' => [
  20. new TagDependency(['tags' =>
  21. ['articles']]),
  22. new DbDependency(['sql' => 'SELECT MAX(id) FROM ' . Account::tableName()])
  23. ]
  24. ],
  25. ],
  26. ];
  27. }
  28. public function actionIndex()
  29. {
  30. $total = Account::find()->sum('amount');
  31. $articles = Article::find()->orderBy('id DESC')->limit(5)->all();
  32. return $this->render('index', array(
  33. 'total' => $total,
  34. 'articles' => $articles,
  35. ));
  36. }
  37. public function actionRandomOperation()
  38. {
  39. $rec = new Account();
  40. $rec->amount = rand(-1000, 1000);
  41. $rec->save();
  42. echo 'OK';
  43. }
  44. public function actionRandomArticle()
  45. {
  46. $n = rand(0, 1000);
  47. $article = new Article();
  48. $article->title = "Title #".$n;
  49. $article->text = "Text #".$n;
  50. $article->save();
  51. TagDependency::invalidate(\Yii::$app->cache,
  52. 'articles');
  53. echo 'OK';
  54. }
  55. }
  1. 完成了。现在,在加载dashboard/index几次以后,你将会看到只有1个查询,如下所示:

使用缓存依赖和chains - 图4

此外,尝试运行dashboard/random-operation或者dashboard/randomarticle,并刷新dashboard/index。数据将会改变:

使用缓存依赖和chains - 图5

工作原理…

为了修改最少的代码,并达到最高的性能,我们使用一个过滤器来作全页缓存:

  1. public function behaviors()
  2. {
  3. return [
  4. 'pageCache' => [
  5. 'class' => 'yii\filters\PageCache',
  6. 'only' => ['index'],
  7. 'duration' => 24 * 3600 * 365, // 1 year
  8. 'dependency' => [
  9. 'class' => 'yii\caching\ChainedDependency',
  10. 'dependencies' => [
  11. new TagDependency(['tags' => ['articles']]),
  12. new DbDependency(['sql' => 'SELECT MAX(id) FROM account'])
  13. ]
  14. ],
  15. ],
  16. ];
  17. }

先前的代码意味着我们在index动作中应用一个全页缓存。这个页面将会缓存1年,并且如果数据改变了,这个缓存将会刷新。因此,一般情况下,依赖工作如下:

  • 按依赖中的描述,第一次运行会获取最新的数据,保存以备之后调用,并更新缓存
  • 按依赖中的描述,获取最新的数据,获取保存的数据,然后比较两者
  • 如果相等,使用缓存的数据
  • 如果不相等,更新缓存,使用最新的数据,并保存最新依赖数据,以备以后调用

在我们的例子中,使用了两个类型的依赖——标签和DB。一个标签依赖使用自定义字符串标签标记数据,并检查它,来决定缓存是否无效,一个DB依赖使用SQL查询结果来达到相同的目的。

现在你可能有的问题是,“为什么有时候使用DB而有时候使用标签?”这是一个好问题。

使用DB依赖的目的是,替换重的计算,并选择一个轻的查询,尽可能获取少的数据。关于这种依赖最好的事情是,我们不需要在已有的代码中嵌入任何额外的逻辑。在我们的例子中,我们可以使用这种类型的依赖用于账户操作,但是不能用于文章,因为文章的内容会改变。因此,对于文章,我们设置一个全局标签,名叫文章,它表示我们可以手动调用来刷新文章缓存:

  1. TagDependency::invalidate(\Yii::$app->cache, 'articles');

参考

欲了解更多关于缓存的信息,并使用缓存依赖,参考http://www.yiiframework.com/doc-2.0/guide-caching-overview.html