Asial Blog

Recruit! Asialで一緒に働きませんか?

Memcacheはやっぱりすごかった

カテゴリ :
バックエンド(プログラミング)
タグ :
森川です。恥ずかしながらmemcacheを使うくらいならtmpfsとかMySQLのHEAPテーブルを使えばいいじゃん、などと思っていたのですが、今回簡単なベンチマークをやってみて心を入れ替えました。

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

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

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

  1. # yum install memcached
  2. # pecl install memcache
  3. # vi /usr/local/lib/php.ini
  4. extension=memcache.so を追加
  5. # cat /etc/sysconfig/memcached
  6. PORT="11211"
  7. USER="nobody"
  8. MAXCONN="1024"
  9. CACHESIZE="64"
  10. OPTIONS=""

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

  1. check_mem.php
  2. <?php
  3. $memcache = new Memcache;
  4. $memcache->addServer("localhost", 11211);
  5. $memcache->flush();
  6. for ($i = 0; $i < 100000; $i++) {
  7.   $memcache->set(md5($i), crc32($i), 0, 36000);
  8. }
  9. check_mem_2.php
  10. <?php
  11. $memcache = new Memcache;
  12. $memcache->addServer("localhost", 11211);
  13. for ($i = 0; $i < 100000; $i++) {
  14.   $memcache->get(md5($i));
  15. }

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

  1. $ time php check_mem.php 
  2. real    0m11.558s
  3. user    0m3.948s
  4. sys     0m2.806s
  5. $ time php check_mem_2.php
  6. real    0m9.599s
  7. user    0m3.203s
  8. sys     0m2.827s

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

  1. テーブル定義
  2. CREATE TABLE check_performance(
  3.   name VARCHAR(32),
  4.   value VARCHAR(255),
  5.   PRIMARY KEY(name)
  6. ) TYPE=HEAP;

  1. check_mysql.php
  2. <?php
  3. include_once "DB.php";
  4. $dsn = "mysql://root@localhost/heap";
  5. $db = DB::Connect($dsn);
  6. $sql = "TRUNCATE TABLE check_performance";
  7. $db->query($sql);
  8. $sql = "INSERT INTO check_performance VALUES (?, ?)";
  9. $sth = $db->prepare($sql);
  10. for ($i = 0; $i < 100000; $i ++) {
  11.   $db->execute($sth, array(md5($i), crc32($i)));
  12. }
  13.  
  14. check_mysql_2.php
  15. <?php
  16. include_once "DB.php";
  17. $dsn = "mysql://root@localhost/heap";
  18. $db = DB::Connect($dsn);
  19. $sql = "SELECT value FROM check_performance WHERE name = ?";
  20. for ($i = 0; $i < 100000; $i ++) {
  21.   $value = $db->getOne($sql, array(md5($i)));
  22. }

  1. 結果
  2. $ time php check_mysql.php 
  3. real    0m58.172s
  4. user    0m33.034s
  5. sys     0m4.086s
  6. $ time php check_mysql_2.php
  7. real    1m54.113s
  8. user    1m10.043s
  9. 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はレプリケーションしても大丈夫なのだろうか、