現在あるシステムで、memcachedのバックアップとしてTokyoTyrantを利用しています。

以下は、主要な部分だけを抜粋したコードです。

$key = 'hello';
//Pecl::memcached
$value = MyMemcached::get($key);
if($value === false):
//memcacheから取得できないときはTokyoTyrantからの取得を試みる
  MyTokyoTyrant::initialize();
  if(MyTokyoTyrant::getEnabled() === true):
    $value = MyTokyoTyrant::get($key);
    $hasTTCache = (!$value) ? false : true;
  else:
    $hasTTCache = false;
  endif;
//TokyoTyrantからも取得できないときはMySQL
  if($hasTTCache === false):
    $value = self::getByIdWithConnection($key);
    MyTokyoTyrant::set($key, $value, 0);
  endif;
  MyMemcached::set($key, $value);
endif;


システムの前提として、

  1. 設備が非常に貧弱である
    • キャッシュサーバはなく、APサーバと同居したプロセスが用途別に1プロセスずつあるのみ
  2. memcachedを利用するプログラムの構成が適切ではない
    • PHPのクライアント「Pecl::Memcache」がmgetに対応していない影響で、1データごとにgetを繰り返す構成になっている。順次「Pecl::Memcached」のgetMultiに置き換え中

という問題点があります。


どちらも現在根本的な修正に向けて作業中ですが、今日始めて明日実装というわけにはいかない(特にサーバの増強)ので、なんとか「誤魔化」さないといけません。というわけで試行錯誤の末、TokyoTyrantを併用する形になっています。
このシステムで起こりうる事象は以下の通りです。

  • サーバの再起動やmemcachedプロセスの再起動によってキャッシュが揮発し、一時的にMySQLへのアクセスが増大した結果、かなりの負荷が生じる
  • memcachedにリクエストが集中した結果、時間帯によっては接続できない現象が多発し、実際にキャッシュはあってもMySQLに問い合わせることが増える

もちろん揮発性の高いメモリキャッシュである以上、こうしたリスクは全て織り込み済みでの実装ではあるんですが、それを回避するためのハードウェアサポートが望めない環境ではなかなか改善できず(本来は、サーバをスケールアウトしてmemcachedの負荷分散をすべきだと思いますし、サーバに余裕があるならrepcachedの導入なども検討すべきです)。


最初はTokyoTyrantだけでも何とかなるかなと思ったのですが、前述のプログラムの問題が残ったままだとTokyoTyrantも同じように接続不能になりますし、I/Oがギリギリのサーバだと運用しにくいという点も見られました(そこまでギリギリの設備で運用するサービスが他にあるかわかりませんけども...)。


そういうわけで試行錯誤した結果、まずmemcachedがリクエストを捌き、処理しきれない分はTokyoTyrantに問い合わせ、そこでもカバーできない場合にのみMySQLに問い合わせるというシステムを作ってたわけです。今のところ上手く機能できているようで、先日プロセスを再起動せざるを得ない事態が起きたときも、TokyoTyrantからバックアップを取得することで比較的スムースな復帰が出来ました。



ただ以下の点には注意すべきだと思います。

  • TokyoTyrantにはガーベッジコレクションがないので、時間と共に肥大化するデータの格納には向いていない
  • キャッシュ管理が煩雑になるので、更新の発生しないデータに絞って運用するとシンプルでよい

ガーベッジコレクションについては、実装方法も公開されていますし、きちんとサポートしてやれば問題ないとは思います。が、この実装に限って言うとあくまで一時的な措置なのでそこまで作り込みたくないのです。

格納するデータについては、2つの異なるシステムに格納されたキャッシュの整合性を保障するのが難しいというか難しくはないけど面倒なので、基本的に更新は行わない方が楽です。自分の環境ではidと値が1対1で結びつき、更新が過去も未来も発生しない(値を修正するときは修正後のデータを新たに格納し新しいidを再設定することで更新する)データが大量にあるので、そのようなデータの場合には向いているかと思いますが、セッション情報やページキャッシュなどを格納する場合には、少し工夫が必要だと思います。



なお、根本的な解決の部分については無事予算が下りたので、徐々に整備していける予定です。出来れば複数サーバと複数プロセスで管理して、memcachedキャッシュが取得できなくても諦められるレベルで運用したいところなんですが、その辺は設備投資と利益率の問題になってブルーになってくるので、あまり考えないこととします。