2007.07.09 Monday
memcached で複数台クラスタを組む時の注意点
複数の Webサーバからキャッシュを利用したいという要望があったので, 今更ながら PHP + memcached という組み合わせを試してみました。
個人的には「Cache_Lite で事足りるんじゃね?」と密かに思っているのですが, 前から memcached は一度試してみようと思ってたので。
インストールは簡単でハマることもなく, memcached と PHP Memcacheモジュールの両方をさくっとインストール完了。
でメモリ 1Gを確保してデーモンとして memcached を起動。
PHP側からは Memcache関数を使うことで簡単にキャッシュの操作ができます。
複数の Webサーバから同じオブジェクトを参照する時等に便利に使えそうです。
memcached は複数台でクラスタ構成を組むことができ, 読み書き共に負荷分散が可能。
Memcache::addServer でサーバを台数分追加して, 一つのクラスタに対して Memcache::set, Memcache::get します。
この例だと host{1..3} で一つの memcachedクラスタを構成して, そのクラスタに対して操作するイメージです。
ただ, 複数台の memcachedクラスタを組むにあたり注意点があります。
個人的には「Cache_Lite で事足りるんじゃね?」と密かに思っているのですが, 前から memcached は一度試してみようと思ってたので。
インストールは簡単でハマることもなく, memcached と PHP Memcacheモジュールの両方をさくっとインストール完了。
$ memcached -d -m 1024
でメモリ 1Gを確保してデーモンとして memcached を起動。
PHP側からは Memcache関数を使うことで簡単にキャッシュの操作ができます。
複数の Webサーバから同じオブジェクトを参照する時等に便利に使えそうです。
memcached は複数台でクラスタ構成を組むことができ, 読み書き共に負荷分散が可能。
Memcache::addServer でサーバを台数分追加して, 一つのクラスタに対して Memcache::set, Memcache::get します。
$m = new Memcache();
$m->addServer("host1", 11211);
$m->addServer("host2", 11211);
$m->addServer("host3", 11211);
$m->set("key", "val");
$m->get("key");
この例だと host{1..3} で一つの memcachedクラスタを構成して, そのクラスタに対して操作するイメージです。
ただ, 複数台の memcachedクラスタを組むにあたり注意点があります。
memcached は書き込みの際にキー配置アルゴリズムによって書き込みを分散します。
"key1", "val1" の組み合わせは host1 へ, "key2", "val2" の組み合わせは host2 へというような感じです。
データベースのレプリケーションのようなイメージではなく, あくまでもどれか一つのサーバに対してしか書き込みされません。
また, 複数台でクラスタ構成をとっていたとしても, memcached はサーバがダウンした場合にそのキャッシュ内容を他のサーバが引き継ぐという仕組みがありません。
仮に host2 がダウンしたら, host2 に乗っているキャッシュは全て消滅して参照できなくなってしまいます。
そして host{1,3} の組み合わせで新たなキー配置アルゴリズムが適用され, 書き込みと参照が分散されます。
# サーバがダウンしてもキー配置アルゴリズムを変更させないことも可能
サーバがダウンしなかったとしても, オペミス等で memcached を kill した場合も同様です。
memcached が死んでデータが参照できなくなったら困る!という場合は, 書き込み時に全てのサーバに対してデータを書き込めば OK です。
上記の関数は Memcache の拡張クラスの一部ですが, イメージ的にはこの関数のような感じです。
これでサーバが落ちても, 突然データが参照できなくなるということはなくなります。
ただ, サーバ復旧時には, サーバが落ちるまでの時点のキャッシュと, 落ちてから復旧するまでのキャッシュの両方を前もって書き込んでおいてやる必要があります。
復旧時のこの処理が実は案外面倒で手間がかかったり。
サーバ間でキャッシュ内容を同期するような機能は欲しいですね...
キャッシュ生成処理がそれほど重くない場合は, サーバが死んだ場合(Memcache::get に失敗した場合)には直接データを取得するようにして復旧まで待てばいいので, わざわざ全部の memcached にデータを書き込む必要はありません。
キャッシュの生成に非常に時間がかかる場合に, 保険として全部の memcached にデータを書き込むのはアリだと思います。
そこら辺は, 書き込むデータ量と参照頻度, キャッシュ生成頻度と相談の上でどうするか考えてください。
まぁ, キャッシュは所詮キャッシュなので, 消えた場合にアプリケーションにとって致命的にならないようなデータを入れるのが大前提ですが( ´∀`)
memcached は高速とのことなので今後積極的に導入したいのですが, 運用の手間とのトレードオフですかね。
"key1", "val1" の組み合わせは host1 へ, "key2", "val2" の組み合わせは host2 へというような感じです。
データベースのレプリケーションのようなイメージではなく, あくまでもどれか一つのサーバに対してしか書き込みされません。
また, 複数台でクラスタ構成をとっていたとしても, memcached はサーバがダウンした場合にそのキャッシュ内容を他のサーバが引き継ぐという仕組みがありません。
仮に host2 がダウンしたら, host2 に乗っているキャッシュは全て消滅して参照できなくなってしまいます。
そして host{1,3} の組み合わせで新たなキー配置アルゴリズムが適用され, 書き込みと参照が分散されます。
# サーバがダウンしてもキー配置アルゴリズムを変更させないことも可能
サーバがダウンしなかったとしても, オペミス等で memcached を kill した場合も同様です。
memcached が死んでデータが参照できなくなったら困る!という場合は, 書き込み時に全てのサーバに対してデータを書き込めば OK です。
function setAll($key, $val, $expire = 0)
{
// ホストが 1台の場合は単に set する
if (count($this->_hostInfo) === 1) {
$this->set($key, $val, $expire);
} else if (count($this->_hostInfo) >= 2) {
// ホストの数だけ個々に Memcache インスタンスを生成し値を set する
foreach ($this->_hostInfo as $hostInfo) {
$memcache = new Memcache();
$memcache->connect($hostInfo['host'], $hostInfo['port']);
$memcache->set($key, $val, $this->_compress, $expire);
}
}
}
上記の関数は Memcache の拡張クラスの一部ですが, イメージ的にはこの関数のような感じです。
これでサーバが落ちても, 突然データが参照できなくなるということはなくなります。
ただ, サーバ復旧時には, サーバが落ちるまでの時点のキャッシュと, 落ちてから復旧するまでのキャッシュの両方を前もって書き込んでおいてやる必要があります。
復旧時のこの処理が実は案外面倒で手間がかかったり。
サーバ間でキャッシュ内容を同期するような機能は欲しいですね...
キャッシュ生成処理がそれほど重くない場合は, サーバが死んだ場合(Memcache::get に失敗した場合)には直接データを取得するようにして復旧まで待てばいいので, わざわざ全部の memcached にデータを書き込む必要はありません。
キャッシュの生成に非常に時間がかかる場合に, 保険として全部の memcached にデータを書き込むのはアリだと思います。
そこら辺は, 書き込むデータ量と参照頻度, キャッシュ生成頻度と相談の上でどうするか考えてください。
まぁ, キャッシュは所詮キャッシュなので, 消えた場合にアプリケーションにとって致命的にならないようなデータを入れるのが大前提ですが( ´∀`)
memcached は高速とのことなので今後積極的に導入したいのですが, 運用の手間とのトレードオフですかね。









Comments
決して使い勝手がいいわけじゃないですもんねぇ...
まぁ, ウチはそこまでの規模ではないので大丈夫( ´∀`)
あんまり頼らないことをお薦めします。
辞めたいなぁ〜。memcached...