複合キーを使用してmemcachedのデータを操作する

多くの場合、 memcachedを使用する場合、さまざまな場所でデータを削除する必要がある状況が発生します。 たとえば、新しいコメントを追加する場合、このページ自体のコメントのキャッシュだけでなく、メインページのコメントフィード、ユーザーコメントのリスト、ユーザーコメントカウンター、一般サイトのコメントカウンター、記事のコメントカウンターなども更新する必要があります。 このデータのすべてのキーを記憶し、これらのキーを使用してdelete()を何度も呼び出すことができます。

<?php
$cache->delete( 'comments_art123' );
$cache->delete( 'comments_tape' );
$cache->delete( 'comments_user123' );
$cache->delete( 'comments_counters_art123' );
$cache->delete( 'comments_counters_user123' );
......
?>


* This source code was highlighted with Source Code Highlighter .


ご存じのように、memachedはデータをフラットに保存します。つまり、1つの値は常に1つの値に対応します。 ネストされたキーは存在しません。 また、たとえばマスクによってキーのグループを削除する方法もありません。 たとえば、次のように実行できれば便利です。$ cache-> delete( 'comments *'); しかし、それは不可能です。

しかし、できないが本当に欲しいなら、あなたはできる;)

目的 :キャッシュからデータをグループ化して削除する機能を実装する
解決策 :特定のグループの遠隔性の兆候を別のキャッシュの場所に保存します。データの可用性の事実は、データ自体の可用性の事実ではなく、兆候の存在の事実によって決まります。 この場所をkeshheaderと呼びましょう。 簡単にするために、通常のハッシュをそこに保存します。 コメント付きの例では、ハッシュは次のようになります。

array(
'comments' => array(
'art123' => array(),
'tape' => array(),
'user123' => array(),
'counters' => array( 'art123' =>array(), 'user123' => array(),),
)
);


* This source code was highlighted with Source Code Highlighter .


したがって、特定のグループを削除済みとしてマークするには、キャッシュキャッシュからこのブランチを削除するだけです。 たとえば、カウンターを更新する場合、キー 'counters'を削除すると、cacheheaderは次のようになります。
array(
'comments' => array(
'art123' => array(),
'tape' => array(),
'user123' => array(),
)
);

* This source code was highlighted with Source Code Highlighter .


これは、データベースからデータを引き出してキャッシュに入れる必要があるという兆候になります。もちろん、キャッシュヘッダーに関連情報を追加します。

必要に応じて、キャッシュキャッシュに他の情報、たとえばデータサイズ、または他の関連要素へのリンクを保存できます。これにより、機能が拡張され、ツリー形式だけでなくネットワーク形式でもデータストレージが実装されます。

したがって、上記を実装するクラスのコード:

<?php

/**
* memcached, .
*
* @author rvk
*/
class MemcacheCacher implements DataCacher {

protected $daemon = null ;
protected $key = '' ;
protected $header = array();
const HEADER_KEY_NAME = 'cacheheader' ;

public function __construct($key) {
$ this ->key = explode( '|' , $key);

$ this ->daemon = new Memcache();
$ this ->daemon->connect( 'localhost' , 11211) or die ( "Could not connect" );

// .
$ this ->header = $ this ->daemon-> get (MemcacheCacher::HEADER_KEY_NAME);
if (!is_array($ this ->header)) {
$ this ->daemon->add(MemcacheCacher::HEADER_KEY_NAME, array(), false , 10000);
$ this ->header = $ this ->daemon-> get (MemcacheCacher::HEADER_KEY_NAME);
}

}

/**
*
*
*
* @param mixed $data
*/

public function set ($data) {
$ this ->createHeaderElement();
$ this ->daemon-> set (implode( '|' ,$ this ->key), $data, false , 10000) or die ( "Failed to save data at the server" );
}

/**
*
*
*
* @return mixed
*/

public function get () {
if ($ this ->exists()) {
return $ this ->daemon-> get (implode( '|' ,$ this ->key));
}

return null ;
}

/**
*
*
*
* @return bool
*/

public function exists() {
$element = $ this ->getHeaderElement();
return is_array($element);
}

/**
* . , .
* ,
*/

public function del() {
if ($ this ->exists()) {
$element = $ this ->setHeaderElement( null );
$ this ->daemon-> set (MemcacheCacher::HEADER_KEY_NAME, $ this ->header, false , 10000);
}
}

/**
*
*/
public function flush() {
$ this ->daemon->flush();
}

/**
*
*
*
* @return mixed
*/

protected function & getHeaderElement() {
$header = & $ this ->header;

foreach ($ this ->key as $subkey) {
if (!isset ($header[$subkey])) {
return $header[$subkey];
}
$header = & $header[$subkey];
}
return $header;
}

/**
*
*
*
* @param mixed $value
* @return mixed
*/

protected function setHeaderElement($ value ) {
$element = & $ this ->getHeaderElement();
$element = $ value ;
$ this ->daemon-> set (MemcacheCacher::HEADER_KEY_NAME, $ this ->header, false , 10000);
}

/**
*
*/

protected function createHeaderElement() {
$header = & $ this ->header;

foreach ($ this ->key as $subkey) {
if (!isset ($header[$subkey])) {
$header[$subkey] = array();
}
$header = & $header[$subkey];
}
$ this ->daemon-> set (MemcacheCacher::HEADER_KEY_NAME, $ this ->header, false , 10000);
}

}
?>

* This source code was highlighted with Source Code Highlighter .


コメントに関連するすべてを更新するには、次のコードを実行するだけです。

$ cache-> delete( 'comments');

またはカウンターのみを更新する

$ cache-> delete( 'comments | counters');

まあ、または単にユーザーカウンター

$ cache-> delete( 'コメント|カウンター| user123');

単体テストでは、クラスの操作について最良の情報が得られます(通常、どのドキュメントよりも優れています)。

<?php
class TestOfMemcacheCacher extends UnitTestCase {

public function setUp() {
$ this ->cache = new MemcacheCacher( 'key' );
$ this ->cache-> set (1);
}

public function testOfCacheExists() {
$cache = new MemcacheCacher( 'key' );
$ this ->assertTrue($cache->exists());
$cache = new MemcacheCacher( 'key1' );
$ this ->assertFalse($cache->exists());
}

public function testOfCacheDelete() {
$cache = new MemcacheCacher( 'key' );
$ this ->assertTrue($cache->exists());
$cache->del();
$ this ->assertFalse($cache->exists());

}

public function testOfCacheGet() {
$cache = new MemcacheCacher( 'key' );
$ this ->assertEqual($cache-> get (), '1' );
$ this ->assertNotEqual($cache-> get (), '2' );
}

public function testOfTreeCacheExists() {
$cache = new MemcacheCacher( 'key|key1' );
$cache-> set (123);
$ this ->assertEqual($cache-> get (), '123' );
$ this ->assertTrue($cache->exists());
$cache = new MemcacheCacher( 'key' );
$cache->del();
$cache = new MemcacheCacher( 'key|key1' );
$ this ->assertFalse($cache->exists());
}

public function tearDown() {
$ this ->cache->flush();
}

}

?>


* This source code was highlighted with Source Code Highlighter .


同様のアプローチが、1日あたり約400万ヒットの負荷の下で、プロジェクトphotofile.ruで正常に機能しました。

PS上記のコードを完全に終了したものとして参照しないでください。 これは一例であり、最適化できますし、そうすべきです。

Source: https://habr.com/ru/post/J64976/


All Articles