なぜRedisと他のDBを比較するベンチマークを公開していないのか
原文は Salvatore Sanfilippo により に公開されました。 このブログを購読する
Redisの速度は新規ユーザーにとってアピールポイントの一つになり得ます。比較「広告」の流行に倣うなら、Redis.ioでいくつかの比較を掲載するのが当然とも思えます。しかし、これには二つの問題があります。一つは目的の問題です。私は開発者にRedisの採用を無理に説得したいわけではありません。私たちはただ、少しでも使いやすいプロダクトを提供するために最善を尽くしているだけです。それを使って人々が仕事を片付けられるなら、それで十分嬉しいのです。そこで私のマーケティングへの意欲は終わりです。さらに、異なるシステムを公平に比較することは、ほとんど常に不可能だという問題もあります。
2つのデータベースを比較して公平な数値を得るには、両者が実に多くのものを共有している必要があります。データモデル、正確な耐久性の保証、データレプリケーションの安全性、パーティション発生時の可用性などなど。しばしば、あるシステムが別のシステムより低いスコアを出すのは、速度を犠牲にしてでも「ほら見て」的な派手さはないものの、実際には非常に重要な特性を提供しているからです。さらに、テストスイート自体も複雑な問題です。異なるデータベースシステムがまったく同じプロトコルを喋るのでない限り、クライアントライブラリの違いだけでも大きな差を生むことがあります。
とはいえ、異論を唱え、異なるデータベースシステムを速度で比較するのはやはり良い考えだと信じる人もいます。例えば昨日、RedisとAerospikeDBのベンチマークがここで公開されました。http://lynnlangit.com/2015/01/28/lessons-learned-benchmarking-nosql-on-the-aws-cloud-aerospikedb-and-redis/。
このベンチマークを例に、ベンチマークがいかに人を惑わす代物かという私の主張を示したいと思います。このベンチマークでは、なぜか巨大なEC2インスタンスが使われています。なんと244GBものRAMを搭載したインスタンスです(!)。R3.8xlargeインスタンスです。私のテストでは、より現実的なm3.mediumインスタンスを使います。
このようなモンスター級のインスタンスを使って、Redisはシングルノードの場合、秒間12万8千回の操作を捌けるとされています。私のEC2インスタンスははるかに非力です。別のEC2インスタンスからredis-benchmarkで、パイプライニングを使わず、同じ100バイトのデータサイズでテストすると、秒間3万2千回という結果でした。つまり、シングルプロセスの場合、私のインスタンスは4倍ほど遅いということになります。
このベンチマーク中に、RedisのINFOコマンドでシステムがどのようにCPUを使っているか見てみましょう。
# CPU
used_cpu_sys:181.78
used_cpu_user:205.05
used_cpu_sys_children:0.12
used_cpu_user_children:0.87
127.0.0.1:6379> info cpu…10秒間のテストの後…
# CPU
used_cpu_sys:184.52
used_cpu_user:206.42
used_cpu_sys_children:0.12
used_cpu_user_children:0.87Redisは約3秒をシステム時間に費やし、ユーザー空間ではわずか約1.5秒しか使っていません。ここで起きているのは、各リクエストで作業の大部分がread()とwrite()の呼び出しに費やされているということです。また、各クライアントが1クエリ1レスポンスというワークロードのため、クライアントごとの各リクエストで完全なRTT(往復時間)を支払うことになります。
では、代わりにパイプライニングを使ったらどうなるか見てみましょう。これはRedisユーザーには非常によく知られ、盛んに活用されている機能です。サーバーの利用率を最大化する唯一の方法ですし、アプリケーションの中でも一度に複数の操作を実行できる場面は通常いくつもあるからです。
32件の操作をパイプラインすると、数値は劇的に変わりました。私の小さなインスタンスでも、シングルコアで秒間25万回を捌けるようになったのです。これは、先ほどのベンチマークで32個の(それぞれがより高速な)コアを使って出したトップの結果の25%に相当します。
CPU時間を見てみましょう。
# CPU
used_cpu_sys:189.16
used_cpu_user:216.46
used_cpu_sys_children:0.12
used_cpu_user_children:0.87
127.0.0.1:6379> info cpu…10秒間のテストの後…
# CPU
used_cpu_sys:190.60
used_cpu_user:224.92
used_cpu_sys_children:0.12
used_cpu_user_children:0.87今回は実際にCPUを使ってデータベースエンジンでクエリを処理しており、コンテキストスイッチで多くの時間を失っているだけではありません。システム時間は約1.5秒、Redisプロセス自体では8.46秒を使っています。
パイプラインの数を減らすと、その中間の結果が得られます。パイプラインが4なら秒間10万回(ベンチマークで使われたより大きなインスタンスなら秒間約40万回に相当します)、パイプラインが8なら秒間18万回、といった具合です。
つまり、この方法でRedisとAerospikeDBをベンチマークして驚くほど似た結果が出るのは、決して偶然ではありません。良くも悪くも、データベース自体をテストしているのではなく、ネットワークスタックとカーネルをテストしているのです。DBがreadとwriteのシステムコールだけで、他に大きな無駄なくクエリを捌けるのであれば、こういう結果になるのです。しかもここでは、メモリに収まるデータを扱っているため、実際のクエリを処理する時間はわずかです(ちなみに、Redisで100kの10Mキーといっても、あのインスタンスに割り当てられたメモリのごく一部しか使いません)。
しかし、これだけではありません。実行できる操作についてはどうでしょうか。GET/SETでRedisをテストするのは、フェラーリを雨の日にミラーがどれだけきれいになるかで評価するようなものです。
Redisのアーキテクチャの根本的な部分として、大きく異なる操作でもコストがほぼ同じだという点があります。では、例えば巨大なFacebookゲームで、終了したゲームのスコアを投稿してリーダーボードを作るケースはどうでしょうか。
同じ単一プロセスでも、クエリが ZADD myscores <random-int> <random-int> の場合は秒間11万回を処理できます。
もっと要求してみましょう。HyperLogLogでカーディナリティを推定しながら、同時に新しい要素を追加し、現在の推定値を読み取るとしたらどうでしょうか。2つのredis-benchmarkプロセスを使います。セットのサイズは今回も1000万です。そこでこのテストでは、ランダムな要素でPFADDを実行するベンチマークと、同じHyperLogLogに対して同時にPFCOUNTを実行するベンチマークを立ち上げました。両方とも同時に秒間25万回を記録し、単一のRedisプロセスで合計秒間50万回という結果でした。
Redisで複雑な操作を行うことは、パイプライニングに似ています。1回のread/writeあたりにより多くのことをさせたいのです。そうしなければ、パフォーマンスはI/Oに支配されてしまいます。
では、いくつか役に立つ指摘をまとめておきます。1)GET/SETのベンチマークは、異なるデータベースシステムを比較する良い方法ではありません。2)より良いパフォーマンス比較は、ユースケースごとに行うことです。特定のユースケースについて、異なるデータモデル、スキーマ、クエリ、戦略を用いて、2つの異なるデータベースシステムで同じアプリの同じトラフィックを捌くのに、どれだけのインスタンスが必要になるかを問うのです。3)実際に多くの人が使うであろうインスタンスタイプでテストすること。巨大なインスタンスタイプは特定のデータベースシステムの非効率を覆い隠してしまう可能性がありますし、いずれにせよ大多数の人が使うものではありません。
私たちはこれからもRedisの高速化に努めていきますし、比較ベンチマークの掲載は引き続き避けていきます。
[Amazon AWSがEC2への無料アクセスを提供してくれたことに感謝します]
記事をランダムに読む
コメント
ログインしてコメントする