はじめに
今回からタイプ毎に代表的なNoSQLデータベースを扱っていきます。まず今回は、揮発性key-valueストアの代表としてmemcachedを取り上げます。
なお、利用したコードやプログラムはgithubに置いてあります。適宜参照してください。
どんなところに使える?
memcachedの特徴は、何といってもデータの揮発性(memcachedサーバを停止すると全データが消えてしまうこと)です[1]。そのため、データが消えても影響が小さいところに利用シーンは限られてきます。一方、連想配列(ハッシュ)のように使えて扱いやすいこと、様々なサイトでの事例があること、などから導入はしやすいです。
具体的な利用シーン
memcachedの具体的な利用シーンとしては、以下の事柄が考えられます。
- RDBMSから取得したデータのキャッシュ
- 消えても大きな影響の無いデータの保存
一般的には(1)のような、あくまでもRDBMSの補助としての利用でしょう。(2)の例として、ユーザのセッション情報などを保存することもありますが、データが消えた場合にログイン状態が解除されてしまうなどの影響があるため注意が必要です。
実際に利用してみる
では、実際にmemcachedを利用してみます。まずはmemcachedサーバを起動します。localhostに3台のmemcachedサーバを別々のポートで起動してみましょう。
今回はmemcache-clientというライブラリを使ってRubyからmemcachedを利用するので、必要に応じてインストールしてください。
今回はサーバを3台起動しましたが、コンストラクタの第1引数に配列で指定してあげるだけで、その3台のmemcachedサーバがアプリケーションから分散キャッシュとして利用できるようになります(以下のコードのdo_initアクション)。とても簡単ですね。
データを保存すると、それぞれのキー(key1, key2, key3, key4)はそれぞれ別のポートに立てたmemcachedに分散して保存されていることがわかります。データの取得時も、保存されたそれぞれのサーバからデータを呼び出してくれます。
Railsではフラグメントキャッシュなどのキャッシュの仕組みが備わっており、memcachedはそのバックエンドで動作させることが多いです。そのため、明示的にmemcachedを利用することは少ないかもしれないですが、よくある利用方法としては上記のコードのshowアクションのような形になります。
まずキャッシュされているかをmemcachedでチェックし、キャッシュされていなければRDBMSからデータを呼び出し、そのデータをmemcachedに保存します。このときsetメソッドを利用し、expiresを設定しています。[]=メソッドはsetメソッドのaliasで直感的ですが、expiresを設定することができません。
どうやって分散しているのか?
memcachedでは、クライアント側の実装によって分散が行われます。
以前は単純にハッシュ値をサーバ台数で割り、その余りでどのサーバに対してデータの保存/読み出しを行うかを決定していましたが、それだとサーバ台数が変更したときにデータの保存/読み出し先が大きく変わってしまい、一時的にではありますがキャッシュミスが頻発し、RDBMSにアクセスが集中していました。
そのため、現在ではConsistent Hashingという考え方でサーバ台数変更時の影響を最小限に留めようというのが一般的です。memcache-clientでも、バージョン1.6からConsistent Hashingに対応しています[2]。サーバ台数を変更した場合もそれにあわせて各サーバにデータの保存/読み出しを振り分けてくれるので、分散させるのはとても簡単です[3]。
手軽に扱うために
開発時など、memcachedにどんなデータが保存されているのか調べたい場合があります。telnetで接続して以下のようにコマンドを発行したり、memcached付属のmemcached-toolを利用することでもわかるのですが、あまり手軽ではありません。
そこで、筆者はmemdump.rbという簡単なプログラムを作成し利用しています。以下のようなコマンドで指定したmemcachedサーバ内のキーの一覧を出力できます。
データキャッシュ時の注意
データをキャッシュするときはなるべく外側の、大きなデータの固まりでキャッシュするようにしましょう。あまり細かい粒度でキャッシュするとキャッシュ管理が複雑になってしまいます。古いデータがいつまでもキャッシュされているような状態になってしまっては意味がありません。
まとめ
memcachedはメモリ上にデータを保持するためサーバを落とすとデータが消えてしまいますが、その分非常に高速に動作します。また、扱いは非常に簡単でスケールさせるのも容易です。重いDB処理のキャッシュとして利用してみてはいかがでしょうか。