タグを使用したZend Memcacheの学習

私が開発者であるプロジェクトはキャッシュを使用します。 すぐに予約したいのですが、プロジェクトは非常に負荷が高く、1日約2,000人です。 データベースから負荷を取り除く便利なソリューションは、memekesの使用でした。 Zend Frameworkのプロジェクト以来、それぞれキャッシュの実装がそれを採用しました。 ただし、最も成功した実装ではなく、タグでの作業がまったくないため、キャッシュを選択的にクリアすることはできませんでした。



選択的キャッシュフラッシュをどのように実装できますか? すべてが非常に単純です。タグ名が書き込まれるコンテナを作成します。それらは他のコンテナのキーになり、キャッシュされたメソッドの識別子ハッシュはそれらに格納されます。 フロントエンドキャッシュの場合、Zend_Cache_Frontend_Classが使用されました。 キャッシュされたメソッドのデータキーのハッシュを生成するのは彼です。



次のようになります。





Tags_Container – , .

MemcacheTag1, MemcacheTag2 MemcacheTagN – , Tags_Container , .








特に実装の説明に入ることなく、既製のソリューションを提供します。

バックエンドファイルMy_Cache_Backend_Memcachedを作成します

 class My_Cache_Backend_Memcached extends Zend_Cache_Backend_Memcached { /** * @const string */ const TAGS_CONTAINER_NAME = 'Tags_Container'; /** * @return array */ protected function _getTagsContainer() { $tagsContainer = $this->load(self::TAGS_CONTAINER_NAME); if (false === $tagsContainer) { $tagsContainer = array(); } if (is_string($tagsContainer)) { $tagsContainer = array($tagsContainer); } return $tagsContainer; } /** * @param $tagName * * @return array */ protected function _getIdsByTag($tagName) { $tagIds = $this->load($tagName); if (false === $tagIds) { $tagIds = array(); } if (is_string($tagIds)) { $tagIds = array($tagIds); } return $tagIds; } /** * Save some string datas into a cache record * * Note : $data is always "string" (serialization is done by the * core not by the backend) * * @param string $data Datas to cache * @param string $id Cache id * @param array $tags Array of strings, the cache record will be tagged * by each string entry * @param bool|int $specificLifetime If != false, set a specific lifetime * for this cache record (null => infinite lifetime) * @return bool True if no problem */ public function save($data, $id, $tags = array(), $specificLifetime = false) { $lifetime = $this->getLifetime($specificLifetime); $tagsLifetime = $this->getLifetime(false); if ($lifetime > $tagsLifetime) { $tagsLifetime = $lifetime; } if ($this->_options['compression']) { $flag = MEMCACHE_COMPRESSED; } else { $flag = 0; } $result = true; if (count($tags) > 0) { $tagsContainer = $this->_getTagsContainer(); $containerChanged = false; foreach($tags as $tagName) { if ($tagName == self::TAGS_CONTAINER_NAME) { Zend_Cache::throwException('Incorrect name tag "' . $tagName . '"'); } if (in_array($id, $tagsContainer)) { Zend_Cache::throwException('The key with id = "' . $id . '" already used in the tags'); } if (!in_array($tagName, $tagsContainer)) { $containerChanged = true; $tagsContainer[] = $tagName; } $tagIds = $this->_getIdsByTag($tagName); if (!in_array($id, $tagIds)) { $tagIds[] = $id; } $result = $result && @$this->_memcache->set( $tagName, array($tagIds), $flag, $tagsLifetime ); } if ($containerChanged) { $result = $result && @$this->_memcache->set( self::TAGS_CONTAINER_NAME, array($tagsContainer), $flag, $tagsLifetime ); } } // ZF-8856: using set because add needs a second request if item already exists $result = $result && @$this->_memcache->set( $id, array($data, time(), $lifetime), $flag, $lifetime ); return $result; } /** * @param string $mode * @param array $tags * * @return array */ protected function _get($mode, $tags = array()) { if (is_string($tags)) { $tags = array($tags); } $tagNames = $this->_getTagsContainer(); switch($mode) { case 'ids': break; case 'tags': $tagNames = array_intersect($tagNames, $tags); break; case 'matching': $tagNames = array_intersect($tagNames, $tags); break; case 'notMatching': $tagNames = array_diff($tagNames, $tags); break; default: Zend_Cache::throwException('Invalid mode for _get() method'); break; } $ids = array(); foreach($tagNames as $tagName) { $ids = array_merge($this->_getIdsByTag($tagName), $ids); } return $ids; } /** * Return an array of stored cache ids * * @return array */ public function getIds() { return $this->_get('ids', array()); } /** * Return an array of stored tags * * @return array */ public function getTags() { return $this->_get('tags', array()); } /** * Return an array of stored cache ids which match given tags * * In case of multiple tags, a logical AND is made between tags * * @param array $tags array of tags * * @return array */ public function getIdsMatchingTags($tags = array()) { return $this->_get('matching', $tags); } /** * Return an array of stored cache ids which don't match given tags * * In case of multiple tags, a logical OR is made between tags * * @param array $tags array of tags * * @return array */ public function getIdsNotMatchingTags($tags = array()) { return $this->_get('notMatching', $tags); } /** * Return an associative array of capabilities (booleans) of the backend * * @return array associative of with capabilities */ public function getCapabilities() { $capabilities = parent::getCapabilities(); $capabilities['tags'] = true; return $capabilities; } /** * @param string $mode * @param array $tags * * @return bool */ protected function _clean($mode, $tags) { $result = false; switch ($mode) { case Zend_Cache::CLEANING_MODE_ALL: $result = $this->_memcache->flush(); break; case Zend_Cache::CLEANING_MODE_MATCHING_TAG: $ids = $this->getIdsMatchingTags($tags); $result = true; foreach($ids as $id) { $result = $result && $this->remove($id); } break; case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG: $ids = $this->getIdsNotMatchingTags($tags); $result = true; foreach($ids as $id) { $result = $result && $this->remove($id); } break; case Zend_Cache::CLEANING_MODE_OLD: case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG: $this->_log(self::TAGS_UNSUPPORTED_BY_CLEAN_OF_MEMCACHED_BACKEND); break; default: Zend_Cache::throwException('Invalid mode for clean() method'); break; } return $result; } /** * @param string $mode * @param array $tags * * @return mixed */ public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array()) { return $this->_clean($mode, $tags); } }
      
      





使い方は?



コード例を示しますが、バックエンドとフロントエンドの設定は別の方法で送信できることを警告します。上記のコードは使用方法を理解するためのものです。



 $frontendName = 'Class'; $backendName = 'My_Cache_Backend_Memcached'; $frontendOptions = array(); $memcacheDataObject1 = new Default_Models_MemcacheData1; $memcacheDataObject2 = new Default_Models_MemcacheData2; $backendOptions = array( 'servers' => array( array( 'host' => '127.0.0.1', 'port' => '11211', 'persistent' => 1, 'weight' => 5, 'timeout' => 5, 'retry_interval' => 15 ) ) ); $frontendOptions['cached_entity'] = $memcacheDataObject1; $cachedObject1 = Zend_Cache::factory( $frontendName, $backendName, $frontendOptions, $backendOptions, false, true ); $cachedObject1->setTagsArray(array('Memcached_Tag1')); $frontendOptions['cached_entity'] = $memcacheDataObject2; $cachedObject2 = Zend_Cache::factory( $frontendName, $backendName, $frontendOptions, $backendOptions, false, true ); $cachedObject2->setTagsArray(array('Memcached_Tag2'));
      
      





標準のMemcachedバックエンドの代わりに、バックエンドクラスMy_Cache_Backend_Memcachedを渡す必要があります。また、$ customBackendNaming = trueを指定する必要があります。これは、Zend_Cache ::ファクトリー呼び出しの6パラメーターです。



Default_Models_MemcacheData1とDefault_Models_MemcacheData2はキャッシュされたクラスであり、完全に同一です。 それらの1つの例を示します。

 class Default_Models_MemcacheData1 { public function cachedMethod() { return rand(111, 999); } }
      
      





コードからわかるように、cachedMethodメソッドを呼び出すたびに、ランダムな値を取得する必要があります。

 for ($i = 0; $i < 3; $i++) { Zend_Debug::dump($memcacheDataObject1->cachedMethod(), 'cached data:'); } for ($i = 0; $i < 3; $i++) { Zend_Debug::dump($memcacheDataObject2->cachedMethod(), 'cached data:'); }
      
      





コードを実行すると、次のようなものが得られます。

cached data: int(468)

cached data: int(676)

ached data: int(721)

ached data: int(182)

cached data: int(414)

cached data: int(561)








次のコードを実行して、実行中のキャッシュを確認します

 for ($i = 0; $i < 3; $i++) { Zend_Debug::dump($cachedObject1->cachedMethod(), 'cached data:'); } for ($i = 0; $i < 3; $i++) { Zend_Debug::dump($cachedObject2->cachedMethod(), 'cached data:'); }
      
      





状況は変わります。おおよそ次のデータが得られます。

cached data: int(901)

cached data: int(901)

cached data: int(901)

cached data: int(865)

cached data: int(865)

cached data: int(865)








将来、状況は変わりません。



キャッシュをクリアするには、次のコードを使用します

 $cachedClass1->clean( Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('Memcached_Tag1') );
      
      





送信されたタグに従ってキャッシュが消去されていることを確認してください



選択的キャッシュクリーニングの問題が解決され、パフォーマンスが向上しました。



*コメントをありがとう。 タグの有効期間は、設定に基づいて計算されるようになりました。または、渡されたパラメーター$ specificLifetimeが長い場合は、取得されます。 また、タグが変更された場合に備えて、一般的なコンテナを変更するためのチェックも追加されています。



ps PHP開発者に警告したいのですが、このコードは自分の責任とリスクで使用できます。 このコードは、特別な場合の万能薬かもしれません。 そして爆発的ではありません。

このコードを使用して、送信されたID自体と同様に、タグ名もキーであることを忘れないでください。



All Articles