memcachedを知り尽くす

第5回memcachedの運用と互換アプリケーション

株式会社ミクシィの長野です。memcachedの連載も今回が最終回になります。前回までmemcachedに直接関連する話題を中心に書いてきましたが、今回はmixiでの事例や運用に関する話題、memcachedの互換アプリケーションについて紹介します。

mixiでの事例

mixiではサービスの初期の頃からmemcachedを利用していました。memcachedはサイトへのアクセスの増加が、データベースのスレーブを増やしていく方法では追いつかないほど急激にのびていく中で導入して行きました。加えてスケーラビリティを向上させていく手段として検証を行い、十分な速度と安定性があることが確認できたことも導入の理由になります。現在ではmemcachedはmixiのサービスを提供していく中で非常に重要なコンポーネントとなっています。

図1 現在のシステムコンポーネント
図1 現在のシステムコンポーネント

サーバ構成と台数

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の運用ノウハウ

これまでmemcachedの内部構造やライブラリなどを紹介してきましたが、それ以外の運用ノウハウをいくつか紹介します。

daemontoolsを利用した起動

通常、memcachedは安定して動作しますが、mixiでは現在の最新バージョンである1.2.5において何度かmemcachedのプロセスが落ちることを経験しています。memcachedが数台、障害を起こしてもサービスに影響がないように作られていますが、memcachedのプロセスが落ちたマシンも再度memcachedを起動すると問題なく動作するので、memcachedのプロセスを監視し、自動的に再起動するようにしています。そこで利用したのはdaemontoolsです。

daemontoolsはqmailの作者であるDJBが作成したUNIX のサービスを管理するツール集で、サービスを起動させ、サービスが落ちたら再起動するsuperviseというプログラムが利用できます。

daemontoolsのインストールはここでは紹介しませんが、mixiでは以下のrunスクリプトでmemcachedを起動しています。

#!/bin/sh

if [ -f /etc/sysconfig/memcached ];then
        . /etc/sysconfig/memcached
fi

exec 2>&1
exec /usr/bin/memcached -p $PORT -u $USER  -m $CACHESIZE -c $MAXCONN $OPTIONS

設定をsysconfigディレクトリに分離しています

監視

mixiではmemcachedの監視にオープンソースの監視ソフトウェア「nagios」を利用しています。

nagiosでは簡単にpluginを自作することもできるので、memcachedの動作の監視をgetやaddを発行して詳しく行うこともできますが、mixiではstatsコマンドを発行してmemcached動作を確認しています。

define command {
command_name                   check_memcached
command_line                   $USER1$/check_tcp -H $HOSTADDRESS$ -p 11211 -t 5 -E -s 'stats\r\nquit\r\n' -e 'uptime' -M crit
}

そのほか、mixiではstatsコマンドの結果をrrdtoolを利用してグラフ化し性能監視を行ったり、毎日メモリの使用量をまとめたレポートを作成してメールにて開発者に共有しています。

memcachedの性能

memcachedは非常に高性能であるというのは連載の中でも紹介してきましたが、実際の性能をmixiの事例から紹介します。ここで紹介するグラフはもっとも極端にアクセスが集中していたmemcachedサーバで、サービスに用いられていたものになります。

図2 リクエスト数
図2 リクエスト数
図3 トラフィック
図3 トラフィック
図4 TCP接続数
図4 TCP接続数

上からリクエスト数、トラフィック、TCPの接続数となります。リクエスト数では最大15000qps、トラフィックでは400Mbpsに達し、その際の接続数では1万コネクションを超えています。このサーバも特別なハードではなく、最初に紹介した通常のmemcachedと同じマシンとなります。この時のCPU利用率ですが、

図5 CPU使用率
図5 CPU使用率

まだidleが残っており余裕があることがわかります。このようにmemcachedの性能は非常に高いので、Webアプリケーションの開発者は気兼ねなく一時的なデータやキャッシュデータの保存場所としてmemcachedを活用していくことができます。

互換アプリケーション

memcachedの実装やプロトコルはシンプルな状態が保たれているため、memcached互換の実装が多く存在します。中でも機能の追加で多いものがmemcachedのオンメモリのデータをディスクに書き出し永続性を実現したり、データの冗長性を確保する拡張です。連載の第3回で紹介しましたが、将来のmemcachedではストレージ層をPluggableにし、このような機能もサポートする拡張が進められています。

memcachedの互換アプリケーションをいくつか紹介します。

repcached
memcachedにレプリケーションの機能をつけるpatchとして提供
Flared
QDBMに保存。非同期レプリケーションやフェイルオーバー等も実装されています
memcachedb
BerkeyDBにデータを保存する実装。message queueの実装のプロジェクトも
Tokyo Tyrant
Tokyo Cabinetにデータ保存。memcachedプロトコル互換だけではなくHTTPでもアクセス可能

Tokyo Tyrantの事例

mixiでは互換アプリケーションの中で、Tokyo Tyrantを利用しています。Tokyo Tyrantは同じく平林氏が作成したTokyo Cabinet DBMのネットワークインターフェイスになります。独自のプロトコルを持ちますが、memcachedとの互換プロトコルやHTTPでもデータのやり取りができます。Tokyo Cabinetは非常に高速に動作するので、データをディスクに書き出すことができる実装ですが、非常に高速です。

mixiではTokyo Tyrantをキャッシュサーバとしてではなく、key-valueの組み合わせを保存するDBMSとして利用しています。導入した箇所はユーザの最終アクセス時間を保存するDBで、mixiのサービスのほぼすべてに関連し、ユーザがページにアクセスするたびにデータをアップデートするので負荷も非常に高くなります。MySQLには非常に重たい処理になり、memcachedのみにデータを保存する形ではデータが失われる可能性があるためTokyo Tyrantが導入されています。クライアントとして新規に開発を行わずに、Cache::Memcached::Fastがそのまま利用できるのも大きな利点です。Tokyo Tyrantの詳細については弊社のエンジニアブログを参考にして頂けると幸いです。

まとめ

今回で「memcachedを知り尽くす」の連載は最後になります。memcachedの基本、内部構成、分散の仕組みと運用などを紹介してきました。memcachedについて興味を持って頂けたら幸いです。mixiのシステムや運用に関する情報は弊社のエンジニアブログにも載って行きますのでぜひ参考にしてください。最後まで読んで頂きありがとうございました。

おすすめ記事

記事・ニュース一覧