memcachedを知り尽くす

第1回memcachedの基本

株式会社ミクシィ 開発部 システム運用グループの長野です。普段はミクシィのアプリケーション運用を担当しております。今回から数回にわたり、最近Webアプリケーションのスケーラビリティの分野で話題になっているmemcachedについて、弊社開発部 研究開発グループの前坂とともに、使い方や内部構造、運用について解説させて頂きます。

memcachedとは

memcachedは、LiveJournalを運営していたDanga Interactive社で、Brad Fitzpatrick氏が中心となって開発されたソフトウェアです。現在ではmixiやはてな、Facebook、Vox、LiveJournalなど、さまざまなサービスでWebアプリケーションのスケーラビリティを向上させる重要な要素になっています。

多くのWebアプリケーションは、RDBMSにデータを格納し、アプリケーションサーバでそのデータを引き出してブラウザ等に表示させています。しかしデータが大量になったり、アクセスが集中すると、RDBMSの負荷があがり、データベースのレスポンスが悪化し、Webサイトの表示が遅延するなど大きな影響がでてしまいます。

そこでmemcachedが活躍します。memcachedは高性能な分散メモリキャッシュサーバです。通常、データベースへの問い合わせ結果を一時的にキャッシュすることで、データベースへのアクセス回数を減らし、動的なウェブアプリケーションの高速化やスケーラビリティの向上のために利用されています。

図1 memcachedの一般的な利用
図1 memcachedの一般的な利用

memcachedの特徴

memcachedは非常に高速に動作する分散キャッシュサーバですが、特徴としては以下のような点が上げられます。

  • シンプルなプロトコル
  • libeventによるイベントハンドリング
  • 内蔵のオンメモリストレージ
  • memcached同士での通信は行わない分散方式

シンプルなプロトコル

memcachedのサーバとクライアントの通信は、XMLなどの複雑なフォーマットは利用せず、シンプルな行ベースのプロトコルによって行われます。行ベースのプロトコルなのでtelnetでmemcachedにデータを保存して、保存したデータを取得することもできます。以下はその例です。

$ telnet localhost 11211
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
set foo 0 0 3     (保存コマンド)
bar               (データ)
STORED            (結果)
get foo           (取得コマンド)
VALUE foo 0 3     (データ)
bar               (データ)

プロトコルについてのドキュメントはmemcachedのソースコード内にあります。以下のURLでも参照することができます。

libeventによるイベントハンドリング

libeventとは、Linuxではepoll、BSD系のOSではkqueueなどのイベントハンドリング機能をラップして、サーバに対するコネクション数の増加に対してもO(1)のパフォーマンスを発揮する共通仕様で利用できるようにしたライブラリになります。memcachedはこのlibevetのライブラリを利用しているので、高いパフォーマンスをLinux、BSD、SolarisなどのOS上で発揮することができます。イベントハンドリングについてはここではあまり詳しく触れませんが、Dan Kegel氏のThe C10K Problemが参考になります。

libevent
http://www.monkey.org/~provos/libevent/
The C10K Problem
http://www.kegel.com/c10k.html

内蔵のオンメモリストレージ

memcachedに保存したデータは、パフォーマンスを向上させるように設計されたmemcached内蔵のメモリストレージに貯められます。データはすべてメモリ上にのみ存在するので、memcachedを再起動したり、OSを再起動するとすべてのデータが消えることになります。またメモリが指定された容量に達すると、LRU(Least Recently Used)に基づいて利用されないキャッシュから自動的に削除されていきます。memcached自体キャッシュのためのサーバとして設計されているので、データの永続化についてはあまり考慮されていません。メモリストレージについての詳しい情報はこの連載の2回目以降に前坂が書きますので、そちらを参考にして頂ければと思います。

memcached同士での通信は行わない分散方式

memcachedは「分散」キャッシュサーバですが、分散に関しての機能はサーバ側には備わっていません。memcached同士で通信を行って情報をシェアすることはありません。どのようにして分散を行うかというと、すべてクライアント側の実装に依存するようになっています。memcachedの分散についてはこの連載の中で取り扱っていく予定です。

図2 memcachedの分散方式
図2 memcachedの分散方式

次はmemcachedの利用方法について簡単に紹介します

memcahedの導入

memchedの導入は比較的簡単です。ここではインストールについて解説します。

memcachedはさまざまなplatformで動作します。

  • Linux
  • FreeBSD
  • Solrais(memcached 1.2.5以上)
  • Mac OS X

また、Windowsにもインストールすることができます。 ここではFedora 8を利用して解説します。

memcachedのインストール

memcachedの動作には、最初に紹介したlibeventというライブラリが必要になります。Fedora 8にはrpmのパッケージが用意されているので、yumコマンドを利用してインストールしてしまいます。

$ sudo yum install libevent libevent-devel

memcachedのソースコードは、memcachedのサイトからダウンロードできます。執筆時の最新バージョンは1.2.5になります。Fedora 8ではmemcachedもrpmが用意されているのですが、 バージョンが古く、ソースから簡単にインストールが可能なので今回はrpmを利用しません。

memcached: download
http://www.danga.com/memcached/download.bml

memcachedの導入は一般的なアプリケーションと同じく、configure、make、make installで完了です。

$ wget http://www.danga.com/memcached/dist/memcached-1.2.5.tar.gz
$ tar zxf memcached-1.2.5.tar.gz
$ cd memcached-1.2.5
$ ./configure
$ make
$ sudo make install

デフォルトでは、/usr/local/bin以下にmemcachedがインストールされます。

memcachedの起動

ターミナル上で次のコマンドを打つとmemcachedが起動します。

$ /usr/local/bin/memcached -p 11211 -m 64m -vv
slab class   1: chunk size     88 perslab 11915
slab class   2: chunk size    112 perslab  9362
slab class   3: chunk size    144 perslab  7281
中略
slab class  38: chunk size 391224 perslab     2
slab class  39: chunk size 489032 perslab     2
<23 server listening
<24 send buffer was 110592, now 268435456
<24 server listening (udp)
<24 server listening (udp)
<24 server listening (udp)
<24 server listening (udp)

debug用のメッセージがでてきましたが、これでTCPのPort 11211をlistenして、最大64MBのメモリーを利用するmemcachedがフォアグラウンドで起動しました。 debugメッセージの内容の多くはストレージについてのメッセージですが詳しくは次回の連載で説明いたします。

デーモンとしてバックグラウンドで起動する場合は、

$ /usr/local/bin/memcached -p 11211 -m 64m -d

とします。

ここで利用したmemcachedの起動オプションの内容は以下のようになります

オプション説明
-p利用するTCPのポート。デフォルトは11211
-m最大のメモリーサイズ。デフォルトは64MB
-vvvery verboseモードで起動してデバックメッセージやエラーをコンソールへ出力
-dmemcachedをデーモンとしてバックグラウンドで起動

memcachedのよく利用する起動オプションは上で紹介した4つになりますが、そのほかにもいくつかのオプションがあります。それらについては、

$ /usr/local/bin/memcached -h

とすると表示されます。memcachedのさまざまな動作を変更できるオプションもありますので一度目を通してみるとよいでしょう。

クライアントライブラリで接続する

memcachedに接続をするクライアントラブイラリには、PerlやPHPをはじめ、さまざまな言語の実装があります。memcachedのサイトに載っている言語だけでも、

  • Perl
  • PHP
  • Python
  • Ruby
  • C#
  • C/C++
  • Lua

などがあります。

memcached: client apis
http://www.danga.com/memcached/apis.bml

ここでは、mixiでも利用しているPerlのライブラリでのmemcachedへの接続方法を紹介いたします。

Cache::Memcachedの利用

Perlのmemcachedクライアントは、

  • Cache::Memcached
  • Cache::Memcached::Fast
  • Cache::Memcached::libmemcached

など、いくつかのモジュールがCPANに公開されています。ここで紹介するCache::Memcachedはmemcachedを作成したBrad Fitzpatrick氏によるもので、memcachedのクライアントとして最も利用されているモジュールだと思われます。

Cache::Memcached - search.cpan.org
http://search.cpan.org/dist/Cache-Memcached/

Cache::Memcachedを用いたmemcachedへの接続

以下のソースコードは、Cache::Memcachedを用いて先ほど起動したmemcachedへ接続する例になります。

#!/usr/bin/perl

use strict;
use warnings;
use Cache::Memcached;

my $key = "foo";
my $value = "bar";
my $expires = 3600; # 1 hour
my $memcached = Cache::Memcached->new({
    servers => ["127.0.0.1:11211"],
    compress_threshold => 10_000
});

$memcached->add($key, $value, $expires);
my $ret = $memcached->get($key);
print "$ret\n";

ここでは、Cache::MemcachedにmemcachedサーバのIPアドレスとオプションを1つ指定してインスタンスを作成しています。Cache::Memcachedでよく用いられるオプションは以下の通りです。

オプション説明
serversmemcachedのサーバとポートを配列で指定
compress_thresholdデータを圧縮する場合の値
namespaceキーに指定したprefixを付ける

なお、Cache::MemcachedではPerlの複雑なデータをStorableモジュールでシリアライズして保存することができるので、ハッシュや配列、オブジェクト等をそのままmemcachedに保持することができます。

データの保存

memcachedへデータを保存するメソッドは、

  • add
  • replace
  • set

になります。3つとも使い方は同じです。

my $add = $memcached->add( 'キー', '', '期限' );
my $replace = $memcached->replace( 'キー', '', '期限' );
my $set = $memcached->set( 'キー', '', '期限' );

期限(秒)をつけてデータをmemcachedに保存します。期限を指定しない場合はmemcachedのLRUに基づいてデータを保持します。この3つのメッソドの違いは、

オプション説明
add同じキーのデータがストレージ上にない場合のみ値を保存
replace同じキーのデータがすでにストレージ上にあった場合のみ値を保存
setaddとreplaceと違い、どんな場合でも値を保存

となります。

データの取得

データの取得は、getとget_multiメソッドを利用します。

my $val = $memcached->get('キー');
my $val = $memcached->get_multi('キー1', 'キー2', 'キー3', 'キー4', 'キー5');

一度にデータを取得する場合は、get_multiを利用します。get_multiを利用すると、memcachedに対して複数のキーを非同期で取りにいくことができます。getをループで処理するよりも数十倍高速にデータを取得できます。

データの削除

データの削除は、deleteメソッドになりますが、1つユニークな機能があります。

$memcached->delete('キー', 'block時間(秒)');

通常1つ目の引数にキーを指定してデータを削除しますが、2つ目の引数を与えることで一定時間、新しいデータを同じキーで保存できないようにすることができます。この機能はキャッシュデータの不整合を防ぐ目的に使うことができます。ただし、setメソッドを利用するとこのブロックに関係なくデータを保存することができますので注意してください。

インクリメントとデクリメント操作

memcached上の特定のキーの値をカウンターのように利用できます。

my $ret = $memcached->incr('キー');
$memcached->add('キー', 0) unless defined $ret;

インクリメントとデクリメントはアトミックな操作になりますが、初期値が入ってない場合に自動的に「0」をいれたりという動作をしません。エラーをチェックして初期値を入れる必要がありまます。また232を超えたときの動作もサーバ側ではチェックしません。

まとめ

今回はmemcachedの簡単な紹介と導入、PerlのクライアントであるCache::Memcachedの利用法について簡単に説明させていただきました。memcachedを使うのはかなり簡単であることが理解して頂けたと思います。

次回はmemcachedの内部構造について前坂が説明いたします。memcachedのインターナルの部分がわかるとmemcachedをどのように活用していったらWebアプリケーションをさらに高速化できるかがわかると思うので、次回もお楽しみにお待ちください。

おすすめ記事

記事・ニュース一覧