RedisとMemcachedに関する補足
私を知っている人ならご存じでしょうが、私は競合製品を悪いものだと考えるような人間ではありません。ユーザーが選択肢を持てることを本当に嬉しく思っているので、Redisとほかの技術を比較するようなことは、めったにしません。
ただし、ユーザーが適切なソリューションを選ぶには、正しい情報を得ていなければならないのも事実です。
今回の記事を書くきっかけになったのは、Sidekiqという人気ライブラリの作者としてご存じかもしれないMike Perhamが公開したブログ記事を読んだことです。SidekiqはRedisをバックエンドとして使っています。ですから、MikeをRedisに「反対する」人だとは、まったく思っていません。それでも、http://www.mikeperham.com/2015/09/24/storing-data-with-redis/で読める彼の記事では、キャッシュ用途なら「(Redisではなく)おそらくMemcachedを使うべきだ」と述べています。つまりMikeは、Redisはキャッシュには向いていないと本当に考えているわけです。彼はその主張を次のように説明しています。
- Memcachedはキャッシュ用に設計されている。
- ディスクI/Oを一切行わない。
- マルチスレッドで動作し、マルチコアにスケールすることで毎秒数十万件のリクエストを処理できる。
上記の主張について取り上げたうえで、これらの文からは読み取れず、私の考えでは大半のキャッシュ利用者やユースケースにとってより重要な情報も、後ほど説明します。
Memcachedはキャッシュ用に設計されている:これは議論にならないので飛ばします。「Redisはキャッシュ用に設計されている」とも言えるからです。この点では両者はまったく同じです。次に進みましょう。
ディスクI/Oを一切行わない:Redisでは、望むならディスクI/Oを完全に無効にして、純粋なインメモリ環境にできます。ただし、本当に必要であれば、たとえば「SHUTDOWN SAVE」を使って、再起動するときだけデータベースを永続化することもできます。要するに、Redisの永続化は、まったく使わない場合でさえ付加価値になるということです。
マルチスレッドである:これは事実です。私の目標の一つには、RedisのI/Oをスレッド化することもあります(基本的にデータアクセス自体はスレッド化されていない、memcachedのような形です)。しかしRedisは、特にパイプラインを使うと、1スレッドあたり毎秒かなりの数のリクエストを処理できます(非常に集中的にパイプラインを使った場合、毎秒50万件という数字は珍しくありません。パイプラインを使わなければ、毎秒約10万オペレーションです)。各Redisインスタンスが同じ構成で、マスターとして動作し、ディスク操作を無効にし、memcachedのシャーディングモデルのようにクライアント側でシャーディングする、という一般的なキャッシュ用途では、1システム上で複数のRedisプロセスを起動しても、それほど大変ではありません。そうすれば、共有するもののないマルチスレッド構成になります。したがって重要なのは、1スレッドあたりで処理できるオペレーション数です。最後に確認したとき、Redisは1スレッドあたりでは少なくともmemcachedと同程度に高速でした。実装は時間とともに変わるので、今日どちらが優位かは入れ替わっているかもしれません。しかし両者とも利用可能なリソースを最大限に活用しようとする傾向があるため、性能はかなり近いはずです。Memcachedのマルチスレッドには、使いやすく管理しやすくなるという利点があるのは確かですが、決定的に重要な要素だとは思いません。
さらに、Mikeはオペレーションの「数」については語っていますが、オペレーションの質には触れていません。RedisやMemcachedのようなシステムでは、実際にインメモリのデータ構造にアクセスするコストと比べて、コマンドのディスパッチやI/Oのコストが支配的になります。つまりRedisでは、単純なGETやSETを実行する場合も、ZRANKのような複雑な操作を実行する場合も、コストはほぼ同じです。しかし複雑な操作なら、アプリケーションの観点では、より多くの処理を実行できます。たとえば、キャッシュされた値を5つ取得する代わりに、小さなLuaスクリプトを送るだけで済むかもしれません。したがって、2つのシステムの実際の「スケーラビリティ」には多くの側面があり、何を実現できるかもその一つです。
Mikeが懸念していることのうち、私が有効だと思うのはマルチスレッドだけです。ただし、Redisをmemcachedの代替として使うという特殊なケースで考えれば、複数のプロセスを実行することで対応できます。あるいは、memcachedと同じような操作を行うだけなら、1スレッドを飽和させること自体が非常に難しいので、単一プロセスで十分かもしれません。
本当の違い
ここからは、2つのシステムの本当の違いについて話しましょう。
メモリ効率
この点では、かつてはMemcachedのほうがRedisより優れていました。単純な文字列から文字列への辞書を表現するために設計されたシステムなら、メモリを効率よく使うのは簡単です。この差は劇的なものではありませんし、もう5年ほど確認していませんが、以前は無視できない差でした。
ただし、長時間稼働するプロセスのメモリ効率を考えると、少し事情が違います。次の節を読んでください。
とはいえ、メモリ効率を本当に評価するなら、Redisで特別な形式にエンコードされた小さな集合値は非常にメモリ効率がよいことも考慮すべきです。たとえば小さな整数の集合は、内部的には8ビット、16ビット、32ビット、または64ビット整数の配列として表現されます。順序付けられているため、存在確認には二分探索を使うことができ、対数時間でアクセスできます。
JSONを使わず、ハッシュでオブジェクトを表現する場合も同じことが言えます。したがって、本当のメモリ効率は、実際のユースケースに基づいて評価しなければなりません。
RedisのLRUとSlabアロケータ
メモリ利用の観点から見ると、Memcachedは完璧ではありません。キャッシュする値のサイズが時間とともに大きく変化するアプリケーションでは、深刻な断片化が発生する可能性が高く、唯一の対処法は再起動になります。この点でRedisの挙動は、はるかに予測しやすいものです。
さらに、RedisのLRUは最近大幅に改善され、現在では実際のLRUを非常によく近似するようになっています。詳しくはhttp://redis.io/topics/lru-cacheを参照してください。私の理解が正しければ、memcachedのLRUは今でもSlabアロケータに従って期限切れになるため、ときには実際のLRUとはかけ離れた挙動になることがあります。ただ、この点については専門家の意見も聞きたいところです。RedisのLRUを試したければ、最近のRedisではredis-cliのLRUテストモードを使って試せます。
スマートなキャッシュ
Redisをキャッシュに使い、Memcachedと同じように扱うだけなら、本来の力を大きく取り逃がしています。私の考えでは、Mikeの記事における最大の誤りはここです。キャッシュしたデータを、もっと便利な形で表現できると気づいた人々が、ますますRedisへ移行しています。何かの最新N件を保持したいですか?上限付きリストを使えばよいのです。キャッシュした人気度のインデックスを取得したいですか?ソート済みセットを使えばよいでしょう。ほかにもいろいろあります。
永続化とレプリケーション
これらが必要なら、非常に重要な資産になります。たとえばこのモデルを使えば、大量の読み取り負荷をスケールさせるのはとても簡単です。永続化による再起動後の復旧や、時間の経過に応じてキャッシュのスナップショットを取得できることなども同様です。ただし、両方の機能がまったく関係しない用途があるのも当然です。ここで言いたいのは、永続化とレプリケーションが重要になる「純粋なキャッシュ」のユースケースも存在する、ということです。
可観測性
Redisは非常に観測しやすいシステムです。大量の内部メトリクスについて詳細なレポートを取得でき、データセットをSCANしたり、オブジェクトの有効期限を監視したりできます。LRUアルゴリズムを調整することもできます。クライアントに名前を付けて、CLIENT LISTでその名前を確認することもできます。「MONITOR」を使ってアプリケーションをデバッグすることもできます。ほかにも高度な機能が数多くあります。これはRedisの利点だと思います。
Luaスクリプティング
Luaスクリプティングは、多くのキャッシュ用途で大きな助けになると思います。たとえば、キャッシュされたJSONデータがある場合、Luaコマンドを使えば、すべてを転送する代わりに単一のフィールドだけを取り出してクライアントに返せます(概念的には、Redisのハッシュを使ってオブジェクトを直接表現しても同じことができます)。
結論
Memcachedは優れたソフトウェアです。私はソースコードを何度も読みましたし、業界に革命を起こした存在でもあります。Redisと比べて自分にとってよりよい選択肢かどうか、ぜひ検討してみてください。ただし、物事はその実態に即して評価しなければなりません。結局のところ、Mikeの記事や、長年にわたって繰り返されてきた非常によく似た記事を読んで、私は少し苛立ちを覚えました。そこで、私の見方を皆さんに示すことにしました。事実関係に誤りがあると思ったら、連絡してください。「EDIT」セクションを追加して、記事を更新します。
記事をランダムに読む