MongoDB:$またはVS $ in-より高速に動作するのは何ですか?

クエリの論理演算で$または$を使用する場合、MongoDBのパフォーマンスの非常に小さな比較があります。 この記事が誰かの作業時間を節約することを願っています。



テストはMongoDB 2.4.9で実行されます

MongoDBにドキュメントのコレクションがあるとしましょう。 本質の理解を単純化するために、たった2つのフィールドを持つドキュメントとします。

$m = new MongoClient('mongodb://mongodb01,mongodb02,mongodba/?replicaSet=pkrs'); $mdb = $m->selectDB('test'); $collection = $mdb->selectCollection('test'); $collection->drop(); $collection->ensureIndex(array('i' => 1, 'j' => 1)); for ($i = 0; $i < 100; ++$i) { for ($j = 0; $j < 100; ++$j) { $collection->insert(array('i' => $i, 'j' => $j)); } }
      
      





コレクションには10Kのドキュメントのみが含まれます。 はい、ここではbatchInsertを使用できましたが、メモの本質の理解を複雑にしたくありません。



最大1000のドキュメントを定期的に(1秒に数回)選択する必要があります。 サンプリング条件は、バインドされていないペアijのセットです。

なぜなら 私は1か月も前にMongoDBを使い始めてから、最初に頭に浮かんだのは次のようなリクエストでした。

 $orArray = array(); for ($i = 0; $i < 10; ++$i) { for ($j = 0; $j < 100; ++$j) { $orArray[] = array('i' => $i, 'j' => $j); } } $query = array('$or' => $orArray);
      
      





ここでデータが整然と並んでいるという事実は単なる一例であり、ビジネスロジックに頭を悩ませることはありません。 現実には、上で述べたように、ペアijは互いに接続されておらず、カオス的になります。

この要求を満たそうとすると、不愉快な驚きから目が広がった-要求は2秒以上完了しました! 上記のコードでは、インデックスが作成されていることがわかります。

これは一般に受け入れられませんでした。

速度が低下するのはネットワークではないことを確認することにしましたが、問題はリクエストにあります。

テストのために、彼はこのリクエストを行います:

 $query = array('i' => array('$lt' => 10), 'j' => array('$lt' => 100));
      
      





データ量の結果は同じですが、クエリはすでに0.01秒で実行を開始しています。

回避策を探す必要があることが明らかになりました。 そして彼は見つかりました。 要求のロジックにより、$またはの代わりに$ inの使用が提案されました。 しかし、値のペアですぐに$を使用する方法を見つけることができませんでした。 そのような方法があれば、そのヒントにとても感謝します。

2つのフィールドに$を入力する方法がわからないため、次のように人工フィールドを導入します(アンダースコア「_」でiとjの値をブラインドします)。

 $collection->ensureIndex(array('ij' => 1)); for ($i = 0; $i < 100; ++$i) { for ($j = 0; $j < 100; ++$j) { $collection->insert(array('i' => $i, 'j' => $j, 'ij' => $i.'_'.$j)); } }
      
      





そして、リクエストは次のようになります。

 $inArray = array(); for ($i = 0; $i < 10; ++$i) { for ($j = 0; $j < 100; ++$j) { $inArray[] = $i.'_'.$j; } } $query = array('ij' => array('$in' => $inArray));
      
      





そして、「ああ、奇跡です!」私たちはわずか0.01秒でデータを取得します(しかし、すべては「2秒以上」で始まりました)。

少しグーグルで、この現象について次の説明を見つけました。$またはMongoDB構造でクエリを実行すると、おそらくいくつかのクエリを実行し、結果を「フリーズ」します。 この声明が正しいかどうかはわかりませんが、まだ別の声明を見つけていません。



PS結論:$または

PPS以下のコードで、時間の測定方法を確認できます。 誰かが知らない場合、find()が呼び出されたときに呼び出しが実行されないことを明確にします! MongoCursorオブジェクトのみが作成されます。 そして、最初のドキュメントをリクエストするときのみ、リクエストそのものです。 そのため、ドキュメントの取得サイクルの最初の反復で時間のカットオフが取られます。

PPPS誰かが自宅でテストを運転することに興味があるなら、ここに全体のソースがあります:
 <?php $m = new MongoClient('mongodb://mongodb01,mongodb02,mongodba/?replicaSet=pkrs'); $mdb = $m->selectDB('test'); $collection = $mdb->selectCollection('test'); $collection->drop(); $collection->ensureIndex(array('i' => 1, 'j' => 1)); for ($i = 0; $i < 100; ++$i) { for ($j = 0; $j < 100; ++$j) { $collection->insert(array('i' => $i, 'j' => $j)); } } $orArray = array(); for ($i = 0; $i < 10; ++$i) { for ($j = 0; $j < 100; ++$j) { $orArray[] = array('i' => $i, 'j' => $j); } } $query = array('$or' => $orArray); testQuery('OR Query', $collection, $query); $query = array('i' => array('$lt' => 10), 'j' => array('$lt' => 100)); testQuery('Range Query', $collection, $query); $collection->drop(); $collection->ensureIndex(array('ij' => 1)); for ($i = 0; $i < 100; ++$i) { for ($j = 0; $j < 100; ++$j) { $collection->insert(array('i' => $i, 'j' => $j, 'ij' => $i.'_'.$j)); } } $inArray = array(); for ($i = 0; $i < 10; ++$i) { for ($j = 0; $j < 100; ++$j) { $inArray[] = $i.'_'.$j; } } $query = array('ij' => array('$in' => $inArray)); testQuery('IN Query', $collection, $query); function testQuery ($testName, $collection, $query) { $cursor = $collection->find($query); $cursor->batchSize(1000); $start = microtime(true); $first = true; foreach ($cursor as $doc) { if ($first) { $time1 = microtime(true); $first = false; } } $time2 = microtime(true); $resultFirst = $time1 - $start; $resultOther = $time2 - $time1; echo "{$testName} - First: {$resultFirst} Other: {$resultOther}<br />\n"; }
      
      







UPD 1 dim_sは、複合インデックス (および上記のテストから、複合インデックスが使用されたことは明らかです)インデックスの代わりに2つに分割することを勧めします。 そのようにすると、クエリ処理速度は約10倍(最大0.2秒)加速しましたが、$が入力されたままのオプションは失われます。



Explainが提供するものを広める:

説明($または複合インデックス)
 /* 0 */ { "clauses" : [ { "cursor" : "BtreeCursor i_1_j_1", "isMultiKey" : false, "n" : 1, "nscannedObjects" : 1, "nscanned" : 1, "nscannedObjectsAllPlans" : 1, "nscannedAllPlans" : 1, "scanAndOrder" : false, "indexOnly" : false, "nYields" : 0, "nChunkSkips" : 0, "millis" : 0, "indexBounds" : { "i" : [ [ 1, 1 ] ], "j" : [ [ 1, 1 ] ] } }, { "cursor" : "BtreeCursor i_1_j_1", "isMultiKey" : false, "n" : 1, "nscannedObjects" : 1, "nscanned" : 1, "nscannedObjectsAllPlans" : 1, "nscannedAllPlans" : 1, "scanAndOrder" : false, "indexOnly" : false, "nYields" : 0, "nChunkSkips" : 0, "millis" : 0, "indexBounds" : { "i" : [ [ 2, 2 ] ], "j" : [ [ 2, 2 ] ] } } ], "n" : 2, "nscannedObjects" : 2, "nscanned" : 2, "nscannedObjectsAllPlans" : 2, "nscannedAllPlans" : 2, "millis" : 0, "server" : "mongodb01:27017" }
      
      







説明($またはiとjの2つの個別のインデックス)
 /* 0 */ { "clauses" : [ { "cursor" : "BtreeCursor i_1", "isMultiKey" : false, "n" : 1, "nscannedObjects" : 100, "nscanned" : 100, "nscannedObjectsAllPlans" : 300, "nscannedAllPlans" : 300, "scanAndOrder" : false, "indexOnly" : false, "nYields" : 0, "nChunkSkips" : 0, "millis" : 1, "indexBounds" : { "i" : [ [ 1, 1 ] ] } }, { "cursor" : "BtreeCursor i_1", "isMultiKey" : false, "n" : 1, "nscannedObjects" : 100, "nscanned" : 100, "nscannedObjectsAllPlans" : 300, "nscannedAllPlans" : 300, "scanAndOrder" : false, "indexOnly" : false, "nYields" : 0, "nChunkSkips" : 0, "millis" : 1, "indexBounds" : { "i" : [ [ 2, 2 ] ] } } ], "n" : 2, "nscannedObjects" : 200, "nscanned" : 200, "nscannedObjectsAllPlans" : 600, "nscannedAllPlans" : 600, "millis" : 2, "server" : "mongodb01:27017" }
      
      







説明(人為的に入力されたインデックスで入力)
 /* 0 */ { "cursor" : "BtreeCursor ij_1 multi", "isMultiKey" : false, "n" : 2, "nscannedObjects" : 2, "nscanned" : 3, "nscannedObjectsAllPlans" : 2, "nscannedAllPlans" : 3, "scanAndOrder" : false, "indexOnly" : false, "nYields" : 0, "nChunkSkips" : 0, "millis" : 0, "indexBounds" : { "ij" : [ [ "1_1", "1_1" ], [ "2_2", "2_2" ] ] }, "server" : "mongodb01:27017" }
      
      







UPD 2 MongoDB 2.6の最新バージョンでテスト実行しました

確かに、元のバージョン(2つのフィールドの複合インデックス)ははるかに高速に動作します! つまり、0.07秒で。 しかし同時に、i_jという形式のインデックスを持つオプションは0.006-0.01秒のままです(つまり、約10倍高速です)



All Articles