MongoDBの3:クエリーの最適化

4271 ワード

前提:テストデータベースには100万件のデータがあり、それぞれ{"user":"user:0-100000","age:"0-100","createAt":new Date()}フィールドが格納されている。

  • 複数のフィールドクエリーに基づいている場合、できるだけ正確なクエリーに使用されるフィールドをインデックスの前に、範囲クエリーフィールドをインデックスの後ろに配置します.

  • たとえば、「user:1080」未満のuserフィールドと40歳の年齢の人をクエリーします.

    db.foo.find({"user":{"$lt":"user: 1080"},"age":40}).hint({"age":1,"user":1})
    

    ×

    db.foo.find({"user":{"$lt":"user: 1080"},"age":40}).hint({"user":1,"age":1})
    
  • 単一、複数のインデックスの場合、マルチフィールドソートはインデックスフィールドを最初のビットに配置し、ソートフィールドを最後に配置する必要があります.

  • たとえば、「user」フィールドの昇順、「age」フィールドの昇順:(単一フィールドインデックス)

    db.foo.find().sort({"user":1,"age":1}).hint({"user":1})
    

    ×

    db.foo.find().sort({"user":1,"age":1}).hint({"age":1})
    

    (マルチフィールドインデックス)

    db.foo.find({"age":21}).sort({"user":-1}).hint({"age":1,"user":1})
    

    ×

    db.foo.find({"age":21}).sort({"user":-1}).hint({"user":1,"age":1})
    
  • MongoDBは、インデックスが追加されたフィールドを指示に従ってソートし、2番目のフィールドに基づいてソートしてもパフォーマンスに影響を与えません.

  • たとえば、21歳のユーザーを検索し、ユーザーの順序に従って並べ替えます.

    db.foo.find({"age":21}).sort({"user":-1}).hint({"age":1,"user":1})
    

    このインデックスには既に秩序があり,逆シーケンスでもMongoDBはageフィールドに基づいて複合条件のエントリを検索して逆ループ出力する.PS:db.collection.EnsureIndex({"age":1,"user":1})MongoDBインデックスは、ageに基づいて昇順され、ageの同じエントリがuserによって昇順ソートされます.(-1は逆順序で並べ替えられます).
  • 範囲に従ってソートするクエリー:たとえば、クエリーの年齢が15-30の間であり、user降順に従って処理:
  • db.foo.find({"age":{"$gt":15,"$lt":30}}).sort({"user":-1}).hint({"age":1,"user":1})// 。
    

    ×

    db.foo.find({"age":{"$gt":15,"$ lt":30}}).sort({"user":-1}).hint({"user":1,"age":1})// 。
    

    メモリでソートしないと、ソートをメモリに置くよりも遅くなります.メモリに大量のデータを並べ替えるとパフォーマンスが犠牲になるのは間違いないが、データが多すぎる(32 MBを超える)とMongoDBがエラーになるので、メモリの中で並べ替えないことをお勧めします.また、クエリー範囲を制限すると、MongoDBは数回のマッチング後にインデックスをマッチングしなくなります.この場合、ソートキーを前に置くのは非常に良いポリシーです.
  • 範囲に基づいてソートする出力を制限するクエリ:
  • db.foo.find({"age":{"$gt":15,"$lt":30}}).sort({"user":-1}).limit(1000).hint({"user":1,"age":1})// 。
    //executionTimeMillis: 16
    //totalKeysExamined: 8693
    

    ×

    db.foo.find({"age":{"$gt":15,"$lt":30}}).sort({"user":-1}).limit(1000).hint({"age":1,"user":1})// 。
    //executionTimeMillis: 495
    //totalKeysExamined: 231689
    

    出力エントリを制限すると、クエリ効率は、メモリ内でソートされないインデックスクエリ時間が16 msで、一致するドキュメントが8693件であることを推奨します.メモリでソートする時間は495 msで、一致するドキュメントは231689件です.このように、返されるエントリ数を制限すると、メモリでソートしないほうがずっと速くなります.実際のアプリケーションでは、範囲クエリーが必要なデータをソートすると、一般的に前の結果が得られるので、{「sortKey」:1,「queryCriteria」:1}というインデックスを使用することをお勧めします.このインデックスは、ソートキーをインデックスの前に、クエリー条件をインデックスの後ろに、このインデックスのソートはメモリでは行われず、クエリーエントリを制限する場合に非常に優れています.もちろん、すべてのデータを取得する必要がある場合は、{"queryCriteria":1,"sortKey":1}を使用します.この方法は比較的速いのでなくしますが、メモリの中でソートされ、32 MBの制限に注意してください.一般的には、{"sortKey":1,"queryCriteria":1}インデックスを使用することをお勧めします.

    まとめ

  • 単一インデックス:正確な条件に一致します.
  • マルチインデックス:
  • // ( ),  --> {"sortKey":1,"queryCerteria":1}
    db.foo.find({"age":{"$gt":15,"$lt":30}}).sort({"user":-1}).limit(1000).hint({"user":1,"age":1})
    
    // ,  --> {"queryCerteria":1,"sortKey":1}
    db.foo.find({"age":21}).sort("user":-1).hint({age:1,user:1})
    
  • $ninは常に全テーブルスキャンを行います.
  • $orは、両方のキーにインデックスを個別に作成してこそ効率的です.
  • {a:1,b:1.....z:1}以降、{a:1},{a:1,b:1},{a:1,b:1,c:1}などは自動的に有効になりますが、サブセット{b:1},{c:1},{d:1}や{a:1,c:1}などは有効になりません.
  • は、$neで指定されたエントリを除いて、他のエントリと同様にすべてスキャンするため、インデックスエントリ全体のスキャンを行う効率が低い.