あなたが私と面識があるなら、私が競合製品があることが悪いと考える人間でないことはご存知でしょう。ユーザーに選択肢があることは本当にいいことだと思っていますし、だからこそ他の技術とRedisを比較するようなことはほとんどしませんでした。
しかし、最適なソリューションを選ぶためには、ユーザーは正しく情報を持たねばならないのも確かです。
この記事を書くのは、有名なライブラリであるSidekiqの作者として知られるMike Perhamが、Redisのバックエンドストレージとしての使い方を書いた記事を読んだのがきっかけです。従って、私はMikeがRedisに「反対」の立場の人だとは考えていません。しかし、彼のブログ記事では、キャッシュには「(Redisではなく)Memcachedを使うべきだと思われる」と彼は書いています。つまり、Mikeは単にRedisはキャッシュに向いていないと信じていて、彼の持論を以下のように書いています。
- memcachedはキャッシュのためにデザインされている
- ディスクI/Oは一切行わない
- マルチスレッドで、複数コアにまたがってスケールして数10万リクエストをさばける
これらの主張についてこれから取り上げます。そして、ここに書かれたことにとどまらないことや、キャッシュに使うユーザーやユースケースの多くにより関係があると私が考える、より多くの情報を提供しようと思います。
「memcachedはキャッシュのためにデザインされている」 : これは特に議論の余地はないのでスキップします。「Redisはキャッシュのためにデザインされている」ということもできます。従って、その意味ではどちらも全く同じと言えます。なので、次に行きましょう。
「ディスクI/Oは一切行わない」 : Redisでは、望むならディスクI/Oを無効にすることが可能で、それによって完全にインメモリな環境を得られます。ただし、必要ならばSHUTDOWN SAVE
のようにリブートの時のみデータベースを永続化することもできます。ここでの結論としては、Redisの永続化機能は、それを全く使わない時でも付加価値になるということです。
「マルチスレッドである」 : これは正しいと言えます。私のゴールは、I/Oに関してのみRedisをスレッド化することです(memcachedのように、データアクセス自体は基本的にはスレッド化しません)。しかしRedisは、パイプライニングを使えばスレッドあたりかなりの量のリクエストをさばくことができます(非常に厳しくパイプライニングした場合は一般的に毎秒50万オペレーション、パイプラインなしで毎秒10万オペレーション)。どのRedisインスタンスも同じでそれぞれがマスタとして動作し、ディスク書き込み無効、「memcachedのシャーディングモデル」のようにクライアントがシャーディングするといったよくあるキャッシングのシナリオでは、システムごとに複数のRedisのプロセスを動かすのも悪くはありません。こういった構成を取ってしまえば、シェアードナッシングなマルチスレッドの仕組みができるので、1スレッドあたりどのくらいの処理ができるかがむしろ重要になってきます。以前、スレッド単位ではRedisは少なくともmemcachedと同じぐらい高速なのは確認しています。時間が経って実装も変わっているので、現在の最新の状況と比べるとそれはそれかもしれませんが、どちらのソフトウェアも使えるリソースを目一杯使う傾向があるので、同じようなパフォーマンスを出すと思って間違いないでしょう。Memcachedのマルチスレッドは、管理者にとってはシンプルになるという点で優位ではありますが、そこまで重大ではないと私は考えます。
まだあります。Mikeは、オペレーションの「クオリティ」に触れずに秒間オペレーション数を論じています。Redisやmemcachedのようなシステムでは、インメモリなデータ構造にアクセスすることよりも、コマンドのディスパッチとI/Oが大きな部分を占めます。従って通常は、RedisがシンプルなGET
やSET
あるいはZRANK
のような複雑な処理をするのは、ほとんど同じコストです。しかし複雑な処理ができるということは、アプリケーションレベルから見てたくさんの処理をしてくれるということです。5つのキャッシュ済みの値をフェッチする代わりに、小さなLuaスクリプトを送ってしまえばいいという話かもしれません。つまり、2つのシステムにおける実際の「スケーラビリティ」は、複数の側面を持っていて、ユーザーが欲しいのはそのどれかひとつということになります。
Mikeの意見で私が唯一正しいと思うのは、Redisをmemcachedの代替品としてみる特別な場合の、マルチスレッドに関する部分です。複数のプロセスを実行するか、あるいはmemcached的な処理をするスレッドを飽和状態にするのは非常に困難とみてシンプルに1スレッドのみを動かすか、ということです。
実際の違い
それでは、2つのシステムの「実際の」違いについて議論します。
メモリの使用効率
これはRedisよりもmemcachedが良かった部分です。文字列から文字列の単純な辞書を表現するようなシステムデザインでは、シンプルな方がメモリの使用効率もよくなります。劇的な違いではない上に、私はもう5年もチェックしてはいないですが、顕著な差ではあるでしょう。
ところが、長く実行されるプロセスにおけるメモリの使用効率を考えると、話は少し変わってきます。次節を見てください。
ただし、本当にメモリ使用の効率性を計測するなら、Redisにおいて非常にメモリ効率のいい、特別にエンコードされた小さく集約済みの値を使うべきです。例えば、小さな整数の集合は、内部的には8、16、32、64のいずれかのビットの整数として表現されます。これらはソート済みのため、存在をチェックする際はバイナリサーチを使うことができるので、対数時間でアクセスされます。
同じことは、JSONをソートし直すかわりにオブジェクトの表現にハッシュを使う場合にも起こります。つまり、本当のメモリ使用効率を計測するには、ユースケースと切っても切り離せないということです。
RedisのLRUとスラブアロケーター
memcachedは、メモリ使用効率の点から見ると完璧とは言えません。時間の経過とともにキャッシュした値のサイズを劇的に変更するようなアプリケーションで使っている場合、激しいフラグメンテーションに遭遇するでしょう。そうなれば、解決策は再起動しかありません。Redisはその点、より決定論的です。
さらに、RedisのLRUは最近非常に改善されていて、現在では本当のLRUのかなりよい近似になっています。これについて詳しくはhttp://redis.io/topics/lru-cacheから確認できます。私の理解が正しいなら、memcachedのLRUはスラブアロケーターを元に期限切れの判定を行うので、本当のLRUとはずいぶんかけ離れた挙動になってしまうことがあります。これについては、その道の専門家の意見を聞きたいところです。RedisのLRUをテストしたいなら、最近のバージョンのRedisに用意されているredis-cli
のLRUテストモードを使うことができます。
より高機能なキャッシュ
Redisをキャッシュとして使いたくて、しかもmemcached風に使うのなら、何かを確実に見落としています。私に言わせれば、これがMikeのブログ記事の最大の間違いです。多くの人がRedisに移行しつつありますが、それはキャッシュデータをより便利な方法で表現できることに気づいたからです。何かの最新のN個を保持しておきたいなら?capped listを使いましょう。キャッシュされた人気度のインデックスを取得したいなら?sorted setを使いましょう。こういった例は他にもあるでしょう。
永続化とレプリケーション
これらの機能を使いたいなら、非常に重要なポイントになります。例えば、このモデルを使うと超高負荷な読み込みをスケールさせるのが非常に簡単になります。また、再起動時に永続化するのも、定期的にキャッシュのスナップショットを取る機能も、同じことが言えます。それぞれの機能が全く見当違いになる使い方を示しておくのも、全くフェアでしょう。私がここで言いたいのは、永続化やレプリケーションが重要になる「純粋にキャッシュするだけ」なユースケースもあるということです。
監視のしやすさ
Redisはとてもとても監視しやすいと言えます。たくさんの内部的なメトリクスを詳細にレポート可能です。データセットのSCAN
ができますし、オブジェクトの有効期限も確認できます。LRUアルゴリズムのチューニングもできます。クライアントに名前付けして、CLIENT LIST
で確認することもできます。アプリケーションのデバッグにMONITOR
を使えます。その他、たくさんの高度な操作ができます。これは間違いなく優位点です。
Luaによるスクリプティング
Luaによるスクリプティングは、多くのキャッシュのユースケースで印象的な役割をすると確信しています。例えばJSON blobをキャッシュしている時、全データを送信する代わりに、Luaのコマンドによってある1フィールドだけを抽出してクライアントに返すことができます(概念上、同様にオブジェクトを表現するのにRedisのハッシュを使うこともできます)。
まとめ
memcachedは素晴らしいソフトウェアで、私は何度もソースコードを読んでいるし、ソフトウェア業界の革命だし、Redisとどちらがいいか比較すべきだと思います。しかし、それぞれがどんなものなのかはしっかり評価しなくてはならず、Mikeのレポートや今まで出てきた似たようなレポートに少々うんざりしてきたのも確かです。それで、私は自分の考えをここで書いておこうと思ったわけです。もし何か事実と異なることを見つけたら、私に知らせてください。「EDIT」の項に記事を更新しようと思います。