Yiiのキャッシュのタグ付け

Yiiでは、キャッシュの関連性をチェックするための依存関係が提供されます。 彼らは確かに多くを許可しますが、いつものように、十分ではありません。 キャッシュをタグでタグ付けして、タグを削除すると、このタグでマークされたキャッシュ全体が古くなるようにしたかったのです。

グーグルは、これに関するいくつかの記事を見つけました:



最初の記事は、単一のタグ依存関係でキャッシュします。

2番目の記事はすでにいくつかのタグに依存していますが、説明されている構造を使用すると、プロジェクトで驚くような効果がもたらされます。 このタグでタグ付けされたレコードよりもタグの寿命が長くなることを期待することは、私にとって少し軽薄に思えます。

適切なソリューションが見つからないため、 Dklab_Cache_Backend_TagEmuWrapperに基づいてYiiのスタイルで独自に作成しました。



実装する必要があるタスクを策定します。

  1. 任意の値に1つ以上のタグを付けることができます(必ずしもそうではありません)。
  2. タグを削除する必要があります
  3. タグが削除されると、このタグでマークされたキャッシュ全体が無関係になります


今実装。

  1. タグの関連性を確認できるように、そのバージョンをタグとともに保存します。
  2. タグ付きレコードとともに、マークされたタグのリストがキャッシュに保存されます。
  3. キャッシュエントリの関連性をチェックするとき、キャッシュエントリをマークするすべてのタグを引き出し、キャッシュに保存されているタグと比較します


最後の3つの条件の意味を指で説明します。

キー「key」と値「value」でキャッシュエントリを保存するとします。 このレコードにタグ「tagA」、「tagB」を付けます。

このようなもの:

$dependency = new \Cache\Tagged\Dependency(array('tagA', 'tagB')); Yii::app()->cache->set('key', 'value', 0, $dependency);
      
      





同時に、3つのエントリがキャッシュに保存されます。



実際、YiiはDependencyオブジェクトのコピーを配列に追加し、後でキャッシュの関連性をチェックします。 (ただし、テキストが乱雑にならないように、ここには反映されていません。)



ここで、キャッシュエントリを読み取るとします。

次の手順が実行されます。

  1. キーを持つレコードが読み取られます。
  2. レコードに接着されたタグが読み取られます
  3. キャッシュから読み取られたタグは、レコード内のタグのコピーと比較されます。
  4. タグが一致しない場合、キャッシュは古いと判断されます


これは非常に簡単です。 そして、ここにコードがあります:

 /** * protected/components/cache/Tagged/Dependency.php */ namespace Cache\Tagged; class Dependency implements \ICacheDependency { //  ,    public $_tags = null; //      \ICache public $_backend; //     public $_tag_versions = null; /** *     ,    */ function __construct(array $tags) { $this->_tags = $tags; } function initBackend() { $this->_backend = \Yii::app()->cache; } /** *        . *              property:_tags */ public function evaluateDependency() { $this->initBackend(); $this->_tag_versions = null; if($this->_tags === null || !is_array($this->_tags)) { return; } if (!$this->_backend) return; $tagsWithVersion = array(); foreach ($this->_tags as $tag) { $mangledTag = Helper::mangleTag($tag); $tagVersion = $this->_backend->get($mangledTag); if ($tagVersion === false) { $tagVersion = Helper::generateNewTagVersion(); $this->_backend->set($mangledTag, $tagVersion, 0); } $tagsWithVersion[$tag] = $tagVersion; } $this->_tag_versions = $tagsWithVersion; return; } /** *  true,     */ public function getHasChanged() { $this->initBackend(); if ($this->_tag_versions === null || !is_array($this->_tag_versions)) { return true; } //          $allMangledTagValues = $this->_backend->mget(Helper::mangleTags(array_keys($this->_tag_versions))); //     dependency. ..  foreach ($this->_tag_versions as $tag => $savedTagVersion) { $mangleTag = Helper::mangleTag($tag); //   "",     if (!isset($allMangledTagValues[$mangleTag])) { return true; } $actualTagVersion = $allMangledTagValues[$mangleTag]; //    ,    if ($actualTagVersion !== $savedTagVersion) { return true; } } return false; } }
      
      







この中毒の助手



 namespace Cache\Tagged; /** * protected/components/cache/Tagged/Helper.php */ class Helper { const VERSION = "0.01"; static private $_cache = null; static public function init(\ICache $cacheId = null) { if ($cacheId === null) { if (self::$_cache !== null) { return true; } //       self::$_cache = \Yii::app()->cache; } else { self::$_cache = $cacheId; } return (self::$_cache !== null); } /** *    *       ,    */ static public function deleteByTags($tags = array()) { if (!self::init()) return false; if (is_string($tags)) { $tags = array($tags); } if (is_array($tags)) { foreach ($tags as $tag) { self::$_cache->delete(self::mangleTag($tag)); } } return true; } /** *       */ static public function mangleTag($tag) { return get_called_class() . "_" . self::VERSION . "_" . $tag; } /** *   mangleTag        * @see self::_mangleTag */ static public function mangleTags($tags) { foreach ($tags as $i => $tag) { $tags[$i] = self::mangleTag($tag); } return $tags; } /** *        */ static public function generateNewTagVersion() { static $counter = 0; $counter++; return md5(microtime() . getmypid() . uniqid('')) . '_' . $counter; } }
      
      







なぜなら コードで名前空間を使用した後、設定でエイリアスを登録する必要があります

 Yii::setPathOfAlias('Cache', $basepath . DIRECTORY_SEPARATOR . 'components/cache');
      
      





また、次のような新しい依存関係を使用できます。

 //        $cache = \Yii::app()->cache; //     $dependency = new \Cache\Tagged\Dependency(array('c', 'd', 'e')); //         $cache->set('LetterA', 'A', 0, $dependency); // ,      var_dump($cache->get('LetterA')); //   (      ) \Cache\Tagged\Helper::deleteByTags(array('d')); // ,       var_dump($cache->get('LetterA'));
      
      





完全に幸福にするために、CActiveRecordデータモデルを透過的にキャッシュします。 (新しいクラスをどこかに適用する必要があります)

次の内容で保護された新しいファイル/ components / ActiveRecord.phpを作成します。

 class ActiveRecord extends CActiveRecord { //    const CACHE_DURATION = 0; protected function beforeFind() { $tags = array($this->tableName()); $this->cache(self::CACHE_DURATION, new \Cache\Tagged\Dependency(array($tags))); parent::beforeFind(); } protected function afterSave() { \Cache\Tagged\Helper::deleteByTags($this->tableName()); parent::afterSave(); } protected function afterDelete() { \Cache\Tagged\Helper::deleteByTags($this->tableName()); parent::afterDelete(); } }
      
      





CActiveRecordの代わりにそれを継承し、データベース接続の減少を観察します



All Articles