アシアルブログ

アシアルの中の人が技術と想いのたけをつづるブログです

Memcacheはやっぱりすごかった

森川です。恥ずかしながらmemcacheを使うくらいならtmpfsとかMySQLのHEAPテーブルを使えばいいじゃん、などと思っていたのですが、今回簡単なベンチマークをやってみて心を入れ替えました。

はい、memcacheは偉大です。すごく速いです。

テストとして10万件のデータをINSERTして、そこから該当するデータを10万件取得します。まずはmemcacheを使用した場合です。

今回はdagレポジトリのRPMmemcachedとソースからインストールしたPHP 5.2.3を使用してpecl installでmemcacheエクステンションをインストールしています。memcachedの設定はデフォルトのままです。



# yum install memcached
# pecl install memcache
# vi /usr/local/lib/php.ini
extension=memcache.so を追加
# cat /etc/sysconfig/memcached
PORT="11211"
USER="nobody"
MAXCONN="1024"
CACHESIZE="64"
OPTIONS=""


これで以下のスクリプトを実行します。



check_mem.php
<?php
$memcache = new Memcache;
$memcache->addServer("localhost", 11211);
$memcache->flush();
for ($i = 0; $i < 100000; $i++) {
  $memcache->set(md5($i), crc32($i), 0, 36000);
}
check_mem_2.php
<?php
$memcache = new Memcache;
$memcache->addServer("localhost", 11211);
for ($i = 0; $i < 100000; $i++) {
  $memcache->get(md5($i));
}


この場合、結果は以下のようになります。



$ time php check_mem.php 
real    0m11.558s
user    0m3.948s
sys     0m2.806s
$ time php check_mem_2.php
real    0m9.599s
user    0m3.203s
sys     0m2.827s


ほんとどINSERTとSELECTにかかる時間が変わっていないです。この結果をMySQLのHEAPテーブルを使った場合と比較してみます。



テーブル定義
CREATE TABLE check_performance(
  name VARCHAR(32),
  value VARCHAR(255),
  PRIMARY KEY(name)
) TYPE=HEAP;




check_mysql.php
<?php
include_once "DB.php";
$dsn = "mysql://root@localhost/heap";
$db = DB::Connect($dsn);
$sql = "TRUNCATE TABLE check_performance";
$db->query($sql);
$sql = "INSERT INTO check_performance VALUES (?, ?)";
$sth = $db->prepare($sql);
for ($i = 0; $i < 100000; $i ++) {
  $db->execute($sth, array(md5($i), crc32($i)));
}

check_mysql_2.php
<?php
include_once "DB.php";
$dsn = "mysql://root@localhost/heap";
$db = DB::Connect($dsn);
$sql = "SELECT value FROM check_performance WHERE name = ?";
for ($i = 0; $i < 100000; $i ++) {
  $value = $db->getOne($sql, array(md5($i)));
}




結果
$ time php check_mysql.php 
real    0m58.172s
user    0m33.034s
sys     0m4.086s
$ time php check_mysql_2.php
real    1m54.113s
user    1m10.043s
sys     0m4.982s


memcacheに比べるとずいぶん遅いです。最後にtmpfsとCache_Liteを使った場合、なのですが… tmpfsを使うと、64MBの割り当ての場合10万件ファイルを作成することができませんでした(しかもHEAPテーブルよりも遅い)。

それ以外にもPostgreSQLでテーブルスペースをtmpfsにするとどうなるかをチェックしたりもしてみました。テーブルとインデックスの領域をtmpfsのテーブルスペースに割り当てても、それほど速度が変わらずでした。。トランザクションログの書き込みがあるから当然INSERTは遅いのですが。。

それと、PHPだけでなくてMySQLの場合は直接SQLを流し込んだりもしてみましたが、PHPから実行するmemcacheのパフォーマンスよりも高くなることはありませんでした。

PEAR::DBを使っていたりでオーバーヘッドがあるので、本当のパフォーマンスのチェックではないかもしれません。

ただ、実際に使うであろうスクリプトを考えると、今回のスクリプトに近い結果になると思います。そういう意味ではPDOとか使ったらもうちょっと速くなったかも。ただ、mysqlコマンドで実行してもINSERTに12秒、SELECTで24秒くらいかかっていたのでmemcacheより速くなることは無いと思います。

さらに良いことに複数台でmemcachedを立ち上げると、キーからmemcachedが自動的に値を保存するサーバを決めてくれるようです。さらにfailoverを有効にしておけば、接続できないサーバに保存することになった値を他のサーバに割り振ってくれます。もちろん接続できてるサーバの値には影響がありません。

セッションハンドラとしてもmemcacheを使用できるので、そっちでも使えそう。

欠点を上げるとすると、ロックができないことでしょうか。インクリメンタルな値に関してはincrコマンドがmemcachedに実装されていますが、それ以外のロックが必要な処理はできません。そんなものをmemcacheに保存するのがそもそもの問題なのですが…

MySQLのHEAPはレプリケーションしても大丈夫なのだろうか、