memcachedをphpで操作する方法を説明します。phpのようなアプリケーションコードからmemcachedを操作する場合は、複数台サーバへの負荷分散機能が備わっています。
前提
公式ドキュメント
参考となる公式ドキュメントを以下に示します。
- memcached.org – wiki
- memcached.org – wiki – TutorialCachingStory
- memcached.org – wiki – ConfiguringClient
- memcached.org – wiki – Commands
- PHP Memcached
動作確認済環境
- Rocky Linux 8.6
- memcached 1.5.22
- php 8.2.0
構成図
以下の構成で動作確認をします。
172.16.1.0/24 +-----------------+ | | ens192 | .10 ens192 | .20 +------+------+ +------+------+ | linux010 | | linux020 | | (Rocky8.6) | | (Rocky8.6) | | (memcached) | | (memcached) | | (php8.2) | | | +-------------+ +-------------+
事前準備
PHP 8.2の環境準備方法
PHP 8.2とPHP memcachedをインストールします。
dnf install https://rpms.remirepo.net/enterprise/remi-release-8.rpm dnf install php82 php82-php-pecl-memcached
memcachedの設定
0.0.0.0に対してListenするように設定し、localhost以外からの接続できるようにします。
cat << EOF > /etc/sysconfig/memcached PORT="11211" USER="memcached" MAXCONN="1024" CACHESIZE="64" OPTIONS="-l 0.0.0.0" EOF systemctl restart memcached.service
コマンド一覧
memcachedにはset, add, get, getsなどのコマンドが用意されており、PHPでMemcached::set()メソッドなどを使用する事でこれらのコマンドを実行できます。各コマンドの説明は本ページでは省略します。各コマンドの意味は以下を参照ください。
単一のmemcachedサーバの場合
接続先の定義
PHPでmemcachedを操作する場合は、以下のように接続先を定義します。
$m = new Memcached(); $m->addServer('172.16.1.10', 11211);
setコマンドの操作例
Memcached::set()メソッドを使用すると、memcachedに対してsetコマンドを発行できます。Memcached::set()メソッドの説明は以下の通りです。
public Memcached::set(string $key, mixed $value, int $expiration = ?): bool
操作例は以下のようになります。
$m->set('int', 99); $m->set('string', 'a simple string'); $m->set('array', array(11, 12)); $m->set('object', new stdclass, 300);
以下のように操作すると、Linux機からphpコードを実行できます。
cat << 'EOF' > sample_code_01.php <?php $m = new Memcached(); $m->addServer('172.16.1.10', 11211); $m->set('int', 99); $m->set('string', 'a simple string'); $m->set('array', array(11, 12)); $m->set('object', new stdclass, 300); EOF php82 sample_code_01.php
ncコマンドなどを用いて、想定通りのデータが格納された事を確認します。ただし、arrayのみバイナリのデータが格納されているため、ncコマンドでの確認が困難な事をご留意ください。
[root@linux010 ~]# echo "get int" | nc 172.16.1.10 11211 VALUE int 1 2 99 END [root@linux010 ~]# echo "get string" | nc 172.16.1.10 11211 VALUE string 0 15 a simple string END [root@linux010 ~]# echo "get array" | nc 172.16.1.10 11211 VALUE array 5 14 END [root@linux010 ~]# echo "get object" | nc 172.16.1.10 11211 VALUE object 5 16 stdClass END [root@linux010 ~]#
getコマンドの操作例
Memcached::get()メソッドを使用すると、memcachedに対してgetコマンドを発行できます。Memcached::get()メソッドの説明は以下の通りです。
public Memcached::get(string $key, callable $cache_cb = ?, int $flags = ?): mixed
操作例は以下のようになります。
var_dump($m->get('int')); var_dump($m->get('string')); var_dump($m->get('array')); var_dump($m->get('object'));
以下のように操作すると、Linux機からphpコードを実行できます。
cat << 'EOF' > sample_code_02.php <?php $m = new Memcached(); $m->addServer('172.16.1.10', 11211); var_dump($m->get('int')); var_dump($m->get('string')); var_dump($m->get('array')); var_dump($m->get('object')); EOF php82 sample_code_02.php
上記コマンドの出力を見ると、確かに想定通りの値が取得できる事が読み取れます。
[root@linux010 ~]# php82 sample_code_02.php int(99) string(15) "a simple string" array(2) { [0]=> int(11) [1]=> int(12) } object(stdClass)#2 (0) { } [root@linux010 ~]#
複数のmemcachedサーバの場合
接続先の定義
複数台のmemcachedを操作する場合は、以下のようにaddServerを複数回実行します。
$m = new Memcached(); $m->addServer('172.16.1.10', 11211); $m->addServer('172.16.1.20', 11211);
このような操作をする事によって、下図に示すような負荷分散を実現できます。
負荷分散の動作確認
それでは実際に負荷分散されるかどうかを確認しましょう。テストデータとして、以下に100件のレコードを登録するサンプルコードを示します。
cat << 'EOF' > sample_code_03.php <?php $m = new Memcached(); $m->addServer('172.16.1.10', 11211); $m->addServer('172.16.1.20', 11211); for ($num = 0; $num < 100; $num++){ $m->set("key" . $num, "value" . $num); } EOF php82 sample_code_03.php
実際に負荷分散されているかどうかを確認するには、そのサーバに格納されているkey一覧を取得するMemcached::getAllKeys()メソッドを使用すると便利でしょう。以下に動作確認をするサンプルコードを示します。
cat << 'EOF' > sample_code_04.php <?php $m10 = new Memcached(); $m10->addServer('172.16.1.10', 11211); $m20 = new Memcached(); $m20->addServer('172.16.1.20', 11211); echo "display 172.16.1.10 keys" . "\n"; var_dump($m10->getAllKeys()); echo "display 172.16.1.20 keys" . "\n"; var_dump($m20->getAllKeys()); EOF php82 sample_code_04.php
上記コードの実行結果は以下の通りです。確かに負荷分散されている事が読み取れます。
負荷分散された結果が均等なハッシュ値に基づかないように見えるのは意図した挙動です。memcachedはConsistent Hashingと呼ばれるアルゴリズムに基づいた負荷分散をします。これは、memcachedサーバが追加や削除された時にkeyに対する負荷分散先が変わりづらくするように配慮されたアルゴリズムです。
[root@linux010 ~]# php82 sample_code_04.php display 172.16.1.10 keys array(58) { [0]=> string(5) "key98" [1]=> string(5) "key97" [2]=> string(5) "key96" [3]=> string(5) "key95" <omitted> display 172.16.1.20 keys array(46) { [0]=> string(5) "key99" [1]=> string(5) "key94" [2]=> string(5) "key90" [3]=> <omitted>
トランザクションの実装例
CAS identifier
memcachedにはデータの更新有無を調査するためのCAS identifierと呼ばれる数値が管理されています。CAS identifierはデータが更新される都度インクリメントされる値です。
memcachedに対してgetsコマンドを発行するとCAS identifierが取得できますが、PHPで操作する場合はMemcached::get()メソッドでCAS identifierを取得できます。以下に示すMemcached::get()メソッドの書式で、第3引数$flagsにMemcached::GET_EXTENDEDを指定すると、CAS identifierを取得できます。
「PHPのドキュメント」に記された「CASトークン」という用語は、「公式ドキュメント」の「CAS identifier」と同じ値を意味します。
public Memcached::get(string $key, callable $cache_cb = ?, int $flags = ?): mixed
それでは実際にCAS identifierを取得してみましょう。キーtran01に設定されたCAS identifierを取得するサンプルコードを以下に示します。
cat << 'EOF' > sample_code_05.php <?php $m = new Memcached(); $m->addServer('172.16.1.10', 11211); $m->set('tran01', 'value01'); var_dump($m->get('tran01', null, Memcached::GET_EXTENDED)); EOF php82 sample_code_05.php
上記コードの出力を見ると、確かにCAS identifierを取得できています。
[root@linux010 ~]# php82 sample_code_05.php array(3) { ["value"]=> string(7) "value01" ["cas"]=> int(143) ["flags"]=> int(0) } [root@linux010 ~]#
casコマンドの実行例
memcachedに対してcasコマンドを実行するには、PHPのMemcached::cas()メソッドを使用します。PHPのMemcached::cas()メソッドの書式は以下の通りです。
public Memcached::cas( float $cas_token, string $key, mixed $value, int $expiration = ? ): bool
Memcached::cas()メソッドでデータを更新するサンプルコードを以下に示します。
cat << 'EOF' > sample_code_06.php <?php $m = new Memcached(); $m->addServer('172.16.1.10', 11211); $m->set('tran01', 'value01'); $return_get = $m->get('tran01', null, Memcached::GET_EXTENDED); echo "display get method return" . "\n"; var_dump($return_get); $return_cas = $m->cas($return_get['cas'], 'tran01', 'value02'); echo "display cas method return" . "\n"; var_dump($return_cas); EOF php82 sample_code_06.php
上記コードの出力を以下に記します。Memcached::cas()メソッドは更新に成功した場合はtrueを返します。
[root@linux010 ~]# php82 sample_code_06.php display get method return array(3) { ["value"]=> string(7) "value01" ["cas"]=> int(144) ["flags"]=> int(0) } display cas method return bool(true) [root@linux010 ~]#