グーグルは、これに関するいくつかの記事を見つけました:
最初の記事は、単一のタグ依存関係でキャッシュします。
2番目の記事はすでにいくつかのタグに依存していますが、説明されている構造を使用すると、プロジェクトで驚くような効果がもたらされます。 このタグでタグ付けされたレコードよりもタグの寿命が長くなることを期待することは、私にとって少し軽薄に思えます。
適切なソリューションが見つからないため、 Dklab_Cache_Backend_TagEmuWrapperに基づいてYiiのスタイルで独自に作成しました。
実装する必要があるタスクを策定します。
- 任意の値に1つ以上のタグを付けることができます(必ずしもそうではありません)。
- タグを削除する必要があります
- タグが削除されると、このタグでマークされたキャッシュ全体が無関係になります
今実装。
- タグの関連性を確認できるように、そのバージョンをタグとともに保存します。
- タグ付きレコードとともに、マークされたタグのリストがキャッシュに保存されます。
- キャッシュエントリの関連性をチェックするとき、キャッシュエントリをマークするすべてのタグを引き出し、キャッシュに保存されているタグと比較します
最後の3つの条件の意味を指で説明します。
キー「key」と値「value」でキャッシュエントリを保存するとします。 このレコードにタグ「tagA」、「tagB」を付けます。
このようなもの:
$dependency = new \Cache\Tagged\Dependency(array('tagA', 'tagB')); Yii::app()->cache->set('key', 'value', 0, $dependency);
同時に、3つのエントリがキャッシュに保存されます。
- 'key' =>配列(配列( 'tagA' =>バージョン( 'tagA')、 'tagB' =>バージョン( 'tagB'))、 'value')
- 'tagA' =>バージョン( 'tagA')
- 'tagB' =>バージョン( 'tagB')
実際、YiiはDependencyオブジェクトのコピーを配列に追加し、後でキャッシュの関連性をチェックします。 (ただし、テキストが乱雑にならないように、ここには反映されていません。)
ここで、キャッシュエントリを読み取るとします。
次の手順が実行されます。
- キーを持つレコードが読み取られます。
- レコードに接着されたタグが読み取られます
- キャッシュから読み取られたタグは、レコード内のタグのコピーと比較されます。
- タグが一致しない場合、キャッシュは古いと判断されます
これは非常に簡単です。 そして、ここにコードがあります:
/** * 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の代わりにそれを継承し、データベース接続の減少を観察します