
Node.JSでmemcachedを操作するためのさまざまなライブラリのパフォーマンスを調査するために行った作業を共有したいと思います。 この研究では、4つの候補者が選択されました。
簡単な説明はソースから直接取得され、オリジナルで提供されます。 これは、バージョンとリンクを含む結果リストです。
- mc v1.0.6-Node.jsのMemcacheクライアント( mc )
- node-memcache v0.3.0-ノード用の純粋なJavaScript memcachedライブラリ。 ( node-memcache )
- node-memcached v0.2.6-Node.js用のフル機能のMemcachedクライアント( node-memcached )
- memjs v0.8.0-MemJSは、memcacheを使用するための純粋なNode.jsクライアントライブラリです。 ( memjs )
他のライブラリもありますが、これらの4つが選択されました。 理由は簡単です。ネットワークに関する情報や言及が他の情報よりも多くありました。
彼らは約1年前に選ばれ、今では手が記事に達しました。 さらに価値のあるライバルをいくつか指摘していただければ幸いです。 必ずテストを投稿してください。
バックストーリーから
私はNode.JSを研究し始め、それを実験し、約1年前に比較的最近実際に書きました。 タスクは非常に面白く設定されており、実際にはプラットフォームの要件はありませんでした。 それから、私は個人的にNode.JSを、私が学んだPHPに代わる「はるかに広い」選択肢として選んだと言うことができます。 言い換えれば、私はそのような選択だけがうれしいです。 これで、2番目のプロジェクトがすでに開始され、Node.JSが正当化されています。 率直に言って、私はJavaScript言語のより深い研究から多くの喜びを得ました。 これは、私がかなり長い間知っているように見えた言語ではないことがすぐに明らかになりました。 そして、同じPHPとは異なり、すべてがエレガントで簡潔であるため、nodeunitを使用してユニットテストを記述しようとすることに驚かされました。
トピックから少し外れています。 もちろん、これらは紛れもない利点です。これについては、Habréや他のリソースですでに説明しています。 しかし、これは多くの内部ライブラリを記述したPHPではありません。あらゆる場合に見知らぬ人がたくさんいます。 結局、PHPでさまざまな複雑さのプロジェクトを10年以上書いた経験です。 繰り返しますが、私はすべてをゼロからやり直さなければなりませんでした。 かつてのように、最初にC ++から真珠に切り替え、次に真珠からPHPに切り替えたとき。 それから私はまた、まったく新しい、なじみのないツールを取り、それを自分自身のために何らかの形でカスタマイズしようとしました。
mongoDbでもRabbitMQでも、あるいは同じMysqlでも、多くのシステムで作業する方法を学ぶ必要がありました。 PHPには存在しなかったため、標準的なものはほとんどありません。 大量のライブラリから最適なライブラリを選択し、オブジェクトに必要な機能をブロックする必要がありました。 だから、一歩一歩、さまざまなツールとライブラリを調べて、ある時点でmemkeshに到達しました。
それでは始めましょう
実験の純粋さのために、これらのすべてのライブラリを単一のインターフェースで囲み、テストスクリプトで必要なオプションを指定するだけで、アクション自体はすべての被験者で同じになります。
インターフェイスは4つのメソッドで構成されます。
Init(cb) -オブジェクトの初期化
Set(key、val、cb) -値の設定
Get(キー、CB) -受信した値
終了() -すべてのアクティブな接続を閉じます
mcライブラリのインターフェイスの実装例は次のようになります。
impl.mc = function(){}; impl.mc.prototype = { Init : function(cb) { var self = this; var Mc = require('mc'); self.mmc = new Mc.Client('<ip>:11211', Mc.Adapter.binary); self.mmc.connect(function() { cb(self); }); }, Set : function(key, val, cb) { this.mmc.set(key, val, {flags: 0, exptime : 100}, cb); }, Get : function(key, cb) { this.mmc.get(key, cb); }, End : function() { this.mmc.disconnect(); } };
残りの3つのライブラリに同じ実装を作成し、それらをimpl.jsに入れます。 それほど小さくないコードを積み上げないように、すぐに予約します。例にはエラー処理を追加しませんでした。 これらは単なるテストであり、もちろん、これは実際のライブラリラッパーで考慮されます。
テストはどのように行われますか。
最初のステップは、次の形式のキーの名前で1000個のランダムな整数値を書き込むことです。
__key_ [libName] _ [0 ... 999]、
libNameは調査中のライブラリの1つの名前です(mc、memcached ...)
時間を測定し、1秒あたりのレコード数をカウントします。 ライブラリの名前はキーに追加され、異なる実装間のテストで重複しないようにします。
次のステップ -録音した後、読み始めます。 範囲内のランダムキーを使用して100k回読み取り、再び、時間を記録し、1秒あたりの読み取り回数をカウントします。
そして、これがすべてのオプションを順番に実行するコード自体です。 コードをできる限り最小化しようとしました。
var cacheImpl = require('./impl'); // // className , function _key(className, i) { return '__key_' + className + '_' + i; } // className (memjs, memcached, memcache, mc) function testPerform(className, cb) { var impl = new cacheImpl[className](); impl.Init(function(pc) { var keysCount = 1000, // - getNum = 100000, // - all = 0, d1, d2; // var startSet = function (cb) { all = keysCount; for (var i = 0;i < keysCount;i++) { (function(num){ setImmediate(function(){ // ~~(Math.random() * 100000) - pc.Set(_key(className, num), ~~(Math.random() * 100000), function(err) { if (--all === 0) return cb(); }); }); })(i); }; }; // var startGet = function (cb) { all = getNum; for (var j = 0;j < getNum;j++) { setImmediate(function(){ pc.Get(_key(className, ~~(Math.random() * 1000)), function(err, res) { if (--all === 0) { cb(); pc.End(); } }); }); }; } // var start = function (cb) { d1 = Date.now(); startSet(function() { d2 = Date.now(); console.log(className + ' Set qps: ' + Math.round(keysCount / (d2 - d1) * 1000)); d1 = Date.now(); startGet(function() { d2 = Date.now(); console.log(className + ' Get qps: ' + Math.round(getNum / (d2 - d1) * 1000)); cb(); }); }); } start(cb); }); } // testPerform('memcache', function(){ testPerform('memcached', function(){ testPerform('memjs', function(){ testPerform('mc', function(){}); }); }); });
私が主に興味を持っていたのは、1秒あたりのレコード数と読み取り数の絶対値ではありませんでした(これもそうですが)。
テストは動作中のクラスターで実行され、書き込み/読み取りは隣接サーバーのmemcachedで実行されました。 サーバーには実際のトラフィックがロードされるため、絶対値に注意を払うことはできません。 相対値は重要です。
合計、スクリプトの実行後、次の結果が得られます。
memcacheセットqps:8929 memcache Get qps:19444 memcachedセットqps:6098 memcached Get qps:8924 memjsセットqps:8850 memjs qpsを取得:12857 mcセットqps:14286 mc get qps:23207
すぐに明らかなリーダーが表示され、書き込みと読み取りにより、リーダーの割合でサインインします。
ザプシの評価:
1.mc 100.00% 2. memcache 62.50% 3. memjs 61.95% 4. memcached 42.69%
読書の評価:
1.mc 100.00% 2. memcache 83.79% 3. memjs 55.40% 4. memcached 38.45%

これらは興味深い結果です。 もちろん、ライブラリは、機能、ライブラリの操作の利便性、memekashクラスタ、接続プールなどの機能によって区別されます。1つのmemekeshサーバーがある場合、意図的にこれらの点を見逃し、最も一般的な状況を取ります。 1台で高速に動作する場合、複数のサーバーで相対的な結果に大きな違いはありません。
整数のみが書き込まれ、読み取りは既存のキーでのみ行われるため(キーがない場合は考慮されない)など、テストは非常に総合的です。しかし、それにもかかわらず、すべてのライブラリは同じ立場にあります。 それでも、結果が役に立つことを願っています。
誰かがライブラリの選択を決定するのを手伝ってくれたり、少なくともすべてのライブラリがパフォーマンスの点で同等に優れているわけではないと考えさせてくれたら嬉しいです。
私は、一貫性、プレゼンテーションスタイル、および記事の改善に役立つすべてのことに対する建設的な批判に耳を傾けたいと思います。 一緒に勉強します。
ここでこのような私の「デビュー」はhabrで あなたが決めるのはどれほど成功したか。