memcachedを知り尽くす
第5回 memcachedの運用と互換アプリケーション
株式会社ミクシィの長野です。memcachedの連載も今回が最終回になります。前回までmemcachedに直接関連する話題を中心に書いてきましたが,今回はmixiでの事例や運用に関する話題,memcachedの互換アプリケーションについて紹介します。
mixiでの事例
mixiではサービスの初期の頃からmemcachedを利用していました。memcachedはサイトへのアクセスの増加が,データベースのスレーブを増やしていく方法では追いつかないほど急激にのびていく中で導入して行きました。加えてスケーラビリティを向上させていく手段として検証を行い,十分な速度と安定性があることが確認できたことも導入の理由になります。現在ではmemcachedはmixiのサービスを提供していく中で非常に重要なコンポーネントとなっています。
サーバ構成と台数
mixiではデータベースやアプリケーションサーバ,画像サーバ,リバースプロクシーサーバなど数多くのサーバを利用しています。memcachedだけでも200台近くのサーバが動作しています。memcachedサーバの代表的なスペックは以下の通りになります。
- CPU:Intel Pentium 4 2.8GHz
- Memory:4GB
- HDD:146GB SCSI
- OS:Linux(x86_64)
このサーバは以前データベースサーバなどに利用されていたマシンでした。CPUの性能が上がりメモリの値段も下がっていくなかで,データベースサーバやアプリケーションサーバなどはより高性能でメモリを多く搭載しているサーバへのリプレイスを積極的に行っています。そうすることでmixi全体で利用するサーバの台数が急激に増えていくことを抑え,管理コストも少なくできます。memcached用のサーバはCPUをさほどつかわないため,入れ替え後のマシンを活用しています。
memcachedのプロセス
1台のmemcachedサーバには,memcachedのプロセスを1つだけ起動しています。memcachedへの割当メモリは3GBに設定し,以下の起動オプションにて動作させています。
/usr/bin/memcached -p 11211 -u nobody -m 3000 -c 30720
OSをx86_64にしているので2GB以上のサイズが確保できます。32bitでは1プロセスあたりのメモリ使用量が2GB以上にできません。2GB以下のプロセスを複数起動することも考えられますが,1サーバあたりのTCPのコネクション数が倍になってしまったり,管理上複雑になるので,mixiでは64bitに統一して運用しています。
また,マシンのメモリが4GBあるにも関わらず,割当を3GBとしているのは,これ以上の割当を行うとメモリがswapする恐れがあるためです。当連載の2回目で前坂がmemcachedのメモリストレージ「slab allocator」の解説をしましたが,memcachedの起動時に指定する割当メモリはmemcachedに保存できるデータの量で,「slab allocator」が確保するメモリや,データを保持するための管理用の領域を含んでいません。そのためmemcachedのプロセスの実際のサイズは割当メモリとして指定した容量よりも大きくなるので注意が必要になります。
mixiではmemcachedに保存するデータは比較的小さいものが多いので,プロセスのサイズは指定した容量よりも目に見えて大きくなります。そのため割当メモリを変更しながら検証を行い,3GBがswapを起こさないサイズであることを確認し,運用しています。
memcachedの利用方法とクライアント
mixiのサービスでは200台規模のmemcachedのサーバを1つのpoolとして扱っています。1サーバあたり3GBの容量があるので,全体として600GB近いサイズの巨大なメモリデータベースが存在することになります。クライアントのライブラリとしてはこれまでこの連載で何度か説明をさせて頂いてきたCache::Memcached::Fastを利用し,このメモリデータベースとやりとりを行います。もちろん,キャッシュの分散方法には連載第4回で紹介したConsistent Hashingを利用しています。
アプリケーションレイヤーでのmemcachedの活用方法は,アプリケーションを開発するそれぞれのエンジニアが決定し実装を行っています。しかし,車輪の再発明やCache::Memcached::Fastのバッドノウハウを防ぐためにCache::Memcached::Fastをwrapするモジュールを用意し,それを利用しています。
Cache::Memcached::Fastでconnectionを維持する
Cache::Memcachedではmemcachedとの接続(ファイルハンドル)をCache::Memcachedパッケージ内のクラス変数に保持します。パッケージ内の変数はCGIのようにプロセスが都度起動ではないmod_perlやFastCGIなどの場合,プロセスが動いている間保持されます。その結果memcachedとの接続は切断されずに維持することができ,TCPのconnectionのオーバーヘッドを減らすとともに,短時間でTCPの接続,切断を繰り返すことによるTCPのportの枯渇を防ぐことができます。
ただし,Cache::Memcached::Fastにはこの機能がなく,モジュールの外部からCache::Memcached::Fastのオブジェクトをクラス変数などに保持して接続を切らないようにする必要があります。
package Gihyo::Memcached;
use strict;
use warnings;
use Cache::Memcached::Fast;
my @server_list = qw/192.168.1.1:11211 192.168.1.1:11211/;
my $fast; ##オブジェクトの保持用
sub new {
my $self = bless {}, shift;
if ( !$fast ) {
$fast = Cache::Memcached::Fast->new({ servers => \@server_list });
}
$self->{_fast} = $fast;
return $self;
}
sub get {
my $self = shift;
$self->{_fast}->get(@_);
}
上記の例では,クラス変数である「$fast」にCache::Memcached::Fastのオブジェクトを保存しています。
共通データの扱いとrehash
mixiのhomeページにあるニュースのような全ユーザに共通するキャッシュデータや,設定情報のようなデータは,多くのページで使用され,その参照回数も非常に多くなります。そのような条件下では特定のmemcachedのサーバにのみトラフィックが集中してしがちです。トラフィックの集中自体は問題にはなりにくいですが,トラフィックが集中していたmemcachedのサーバに障害が起きてmemcachedに接続ができない状態になると非常に大きな問題となります。
連載の第4回で少し触れましたが,Cache::Memcachedではデータを保存するサーバに接続ができない場合,再度hash値を計算して別のサーバへ接続をするrehashという機能がありますが,Cache::Memcached::Fastではその機能がありません。代わりにサーバへの接続が連続して失敗した場合にそのサーバへの接続をしばらく行わないようにする機能があります。
my $fast = Cache::Memcached::Fast->new({
max_failures => 3,
failure_timeout => 1
});
failure_timeoutで指定した秒数の中でmax_failures以上の回数,接続に失敗すると,そのmemcachedサーバに接続を行わないようになります。ここでは,1秒間で3回以上に設定しています。
このほか,mixiでは全ユーザに共通するキャッシュデータのキーの命名規則を設け,その命名規則にあったデータの場合は,自動的に複数のmemcachedサーバへデータを保存し,その中から1箇所をのみを選び取得するようなライブラリを構築し,memcachedサーバに障害が起きたことによりほかに影響がでないように工夫しています。
memcachedを知り尽くす
- 第5回 memcachedの運用と互換アプリケーション
- 第4回 memcachedの分散アルゴリズム
- 第3回 memcachedの消去メカニズムと今後の動向
- 第2回 memcachedのメモリストレージを理解する
- 第1回 memcachedの基本


