「memcachedの活用と運用 実践編」の連載も今回が最後となります。連載の第1回 ではmemcachedの最新バージョンである1.4系で増えたオプションやよく利用されるオプションの紹介をしました。第2回目 では安全にmemcachedを利用するために気を配るセキュリティや脆弱性について説明し、3回目 では稼働監視やリソースモニタリングについて書かせて頂きました。
最終回では、これまで説明してこなかったmemcachedを快適に活用、運用するための小さめのTipsをいくつか紹介します。
指定したキーが含まれるサーバを探す
複数台のmemcachedのサーバを1つのグループとしてWebアプリケーションサーバなどのクライアントから利用している場合、特定のキーのデータがどのmemcachedサーバに保存されているのか知ることは容易ではありません。
memcachedのキャッシュオブジェクトの分散は、連載の「memcachedを知り尽くす」の第4回 でも紹介しているように、すべてクライアント側の実装によって実現されています。memcachedのクライアントライブラリは、getやsetと言った命令で渡されたキャッシュオブジェクトのキーに対して、一定のアルゴリズムを適用し、複数台のmemcachedサーバの中からキャッシュオブジェクトを保存するサーバを選択します。
アクセスの多いWebサービスではアプリケーションサーバが複数台になり、memcachedのクライアントライブラリも複数台にインストールがされますが、キャッシュを分散するためのアルゴリズムとmemcachedサーバの一覧をWebアプリケーションサーバで共通にすることで、同じキーが別のmemcachedクライアントからでも同じmemcachedサーバに保存されることを保証しています。
memcachedを知り尽くす 第4回 memcachedの分散アルゴリズム
http://gihyo.jp/dev/feature/01/memcached/0004
memcachedクライアントでよく利用される分散アルゴリズムとして剰余による分散とConsistent Hashingがあります。両者のアルゴリズムの詳細については上記の連載にて紹介しています。どちらのアルゴリズムを利用した場合でも事前にキャッシュオブジェクトがどのmemcachedサーバに保存されるのかを知ることは容易ではありません。しかし、memcachedサーバのキャッシュを保存するslabが全て埋まっているなど、なんらかの不具合で特定のキャッシュオブジェクトが保存できない場合、複数台のmemcachedサーバからどのサーバで不具合が起きているのかを探し出して、調査する必要があります。そこで次のようなツールを作成し、キャッシュオブジェクトを検索することがあります。
リスト search_key.pl
#!/usr/bin/perl
use strict;
use warnings;
use Cache::Memcached::Fast;
my $KEY = $ARGV[0];
my @servers = <STDIN>;
for my $server ( @servers ) {
chomp $server;
my $memd = Cache::Memcached::Fast->new({
servers => [$server],
});
print "Found at $server\n" if $memd->get($KEY);
}
図 実行例
$ cat list.txt
192.168.67.35:11211
192.168.67.36:11211
192.168.67.37:11211
$ cat list.txt | perl check.pl [探すキー]
Found at 192.168.67.36:11211
search_key.plは渡されたmemcachedサーバのリストに対して一台一台接続を行い、引数で指定されたキャッシュオブジェクトをgetしてデータが保存されているかを確認します。障害時にはこのツールを利用して特定したmemcacehdサーバに対してstatsなどでさらに詳しい調査を行います。
共有キャッシュ
Webサイトにおいて、ユーザへのお知らせなどをすべてのページに同じ表示したり、認証や新機能のクローズトテストのために、すべてのリクエストで特定のフラグをチェックすることがあると思われます。お知らせの文言やフラグはアプリケーションのコードの中に埋め込むこともできますが、それではお知らせを修正するたびにデプロイを行う必要があるなど大きな手間となります。そこで必要な情報をデータベースに格納し、そのデータを参照時にmemcachedにキャッシュすることで柔軟性とスケーラビリティを確保することはよく行われています。
しかし、Webサイトのトラフィックが増大してくると、この多くのページで参照する情報を格納するmemcachedサーバに対して大量のトラフィックが集中してしまう危険性があります。大規模なサイトではその通信量だけでも数百Mbpsにもなることがあります。
前節で紹介した通り、memcachedクライアントの分散アルゴリズムにより、特定のキーに対するmemcachedサーバは1台に決定されるため、全ページで取得するようなキャッシュオブジェクトがあると、その一台に対して大量のアクセスが集中してしまいます。そこで多くのページで利用するような共有キャッシュは通信を分散させ、負荷が集中しないようにする必要がでてきます。
sub set {
my $self = shift;
my $key = shift;
for ( 1..30 ) {
my $spread_key = $key . ":" . $_;
$memd->set($spread_key, @_);
}
}
sub get {
my $self = shift;
my $key = shift;
my $spread_key = $key . ":" . int(rand(30)+1);
$memd->get($spread_key);
}
上記のソースコードでは、与えられたキーに対して、set時には「:1」から「:30」までのキーワードを追加して30個のキャッシュに複製、分散保存します。取得時は1から30までの数字をランダムに選びキャッシュキーに追加してmemcachedサーバからの取得を行います。このようにキーワードを追加し、キャッシュを複製、分散することで特定のmemcachedサーバだけにリクエストが偏ることを防いでいます。
安全なincrement
現在のmemcachedの最新バージョンである1.4.5ではマルチスレッド動作時にincrコマンドが正しく行われないバグがあることが知られています。incrコマンドを繰り返すと数値の繰り上がりにより桁数が増えます。桁数が変わるとキャッシュオブジェクトのサイズが変わり、memcached内部のスラブの移動が必要となります。その際の動作がスレッドセーフではないため、スラブ移動中にインクリメントした情報が失われることがあります。
Issue 127: incr/decr operations are not thread safe.
http://code.google.com/p/memcached/issues/detail?id=127
バッドノウハウですが、incrコマンドを発行する際に十分な大きなスラブにデータが保存されるように細工することで、現在のmemcachedのバージョンでも問題を回避できます。
sub counter {
my $self = shift;
my $key = shift;
my $result = $memd->incr($key, @_); -----(1)
if ( defined $result && ! $result ) {
my $init = shift || 1;
$memd->add($key, sprintf("%-20d", 0), @_ ); -----(2)
$result = $memd->incr($key, $init, @_ );
}
$result;
}
上記のコードではmemcachedのincrメソッドを利用してカウンターを実装しています。まず(1)でincrを実行し、値が取得できなければ、(2)で初期値をmemcachedに保存します。その際に、空白を入れて全部で20文字のキャッシュオブジェクトを生成します。20文字あれば2の64乗までの数値が全て入るため、どんなにincrを繰り返してもキャッシュオブジェクトのサイズが変わらないので、memcachedサーバ上のスラブが移動することがなくなり、バグを回避できます。
開発コミュニティ
最後にmemcachedの開発について紹介します。memcachedはオープンソースのソフトウェアであり、多くのエンジニアが参加するIRCやメーリングリスト、ITS(Issue Tracking System)などを活用しながらコミュニティによって開発が行われています。IRCのチャンネルやメーリングリストの参加方法、過去のメーリングリストのアーカイブはmemcached公式Webサイトにまとめられています。また、公式サイト上のWikiにもmemcachedの利用や運用に関する情報がありますので、memcachedを利用してみようと思う方は一度は覗いて見るのをお勧めします。
memcached - a distributed memory object caching system
http://memcached.org/
まとめ
現在memcachedは、数多くのWebサービスで利用され、スケーラビリティの向上、レスポンスの高速化など大きな役割を果たしています。先日(2010年8月)のmixiでの障害によってその重要さが再認識されたところだと思われます。この連載は今回で終了しますが、memcachedの効率的な利用法、運用に関する最新情報はこれからさまざまな場所で紹介して行きます。最後まで読んで頂きありがとうございました。