クエリの最適化は、クエリ操作で加工する必要があるデータ量を減らすことで、読み取り操作の効率を向上させます。インデックス、プロジェクション、クエリ制限を使用して、クエリのパフォーマンスを向上させ、リソース消費を削減します。
クエリをサポートするインデックスの作成
よく実行されるクエリにインデックスを作成します。クエリが複数のフィールドを検索する場合は、複合インデックスを作成します。インデックスを使用するとパフォーマンスが向上します。これは、インデックスがない場合、クエリはコレクション内のすべてのドキュメントをスキャンする必要があるためです。
例、moviesコレクションの ratedフィールドに対する次のクエリを考えてみましょう。
let ratingValue = <someUserInput>; db.movies.find( { rated: ratingValue } );
このクエリのパフォーマンスを向上させるには、ratedフィールドの moviesコレクションにインデックスを追加します。[1] mongosh で、db.collection.createIndex() メソッドを使用してインデックスを作成します。
db.movies.createIndex( { rated: 1 } )
クエリのパフォーマンスを分析するには、説明計画結果の解釈を参照してください。
| [1] | 単一フィールドインデックスの場合、インデックスの順序は関係ありません。複合インデックスの場合、フィールドの順序はインデックスがサポートするクエリに影響します。詳細については、複合インデックスのソート順序を参照してください。 |
選択性のあるクエリの作成
クエリの選択性とは、クエリ述語がコレクション内のドキュメントをどの程度適切にフィルタリングするかを指します。クエリの選択性によって、クエリがインデックスを効果的に使用できるかどうかが決まります。
選択性の高いクエリほど、一致するドキュメントの割合は少なくなります。たとえば、ユニークな _id フィールドでの等価一致は、多くとも 1 件のドキュメントにしか一致しないため、選択性が非常に高くなります。
選択性の低いクエリはドキュメントの大きな割合に一致するため、インデックスを効果的に使用できません。
たとえば、不等式演算子 $nin と $ne は、インデックスの大部分と一致することが多いため、あまり選択的ではありません。その結果、多くの場合、インデックス付きの $nin クエリまたは $ne クエリのパフォーマンスは、コレクション内のすべてのドキュメントをスキャンする必要がある $nin クエリまたは $ne クエリのパフォーマンスと同程度になる可能性があります。
regular expressions の選択性は式自体に依存します。詳細については、「正規表現とインデックスの使用」をご覧ください。
プロジェクトに必要なデータのみ
ドキュメントの一部のフィールドセットが必要な場合は、必要なフィールドのみを返すことでパフォーマンスを向上できます。プロジェクションにより、ネットワーク トラフィックと処理時間が削減されます。
例、moviesコレクションへのクエリに year、title、directors、plot フィールドのみが必要な場合は、プロジェクションでそれらのフィールドを指定します。
db.movies.find( {}, { year: 1, title: 1, directors: 1 } ).sort( { year: -1 } ).limit(3)
$project集計ステージを使用する場合、それは通常パイプラインの 最後のステージ であり、クライアントに返すフィールドを指定するために使用されます。
$project ステージをパイプラインの開始または中間で使用して、後続のパイプラインステージに渡されるフィールドの数を減らしてもパフォーマンスが改善する可能性はほとんどありません。データベースではこの最適化が自動的に実行されているためです。
プロジェクションの使用方法の詳細については、「クエリから返されるプロジェクト フィールド」を参照してください。
例
カバード クエリを実現するには、プロジェクション フィールドにインデックス必要があります。ESR(Equality, Sort, Range)ルールは、インデックス内のフィールドの順序に適用されます。
例えば、moviesコレクションの次のインデックスについて考えてみます。
db.movies.createIndex( { rated: 1, _id: 1, "imdb.rating": 1, title: 1, released: 1 } )
上記のインデックス は技術的には正しいですが、クエリ パフォーマンスを最適化するように構成されていません。
次のクエリでは、ESR(Equality(等価)、Sort(並べ替え)、Range(範囲))ルールを使用してより効率的な集計パイプラインを構築し、クエリの応答時間を短縮します。
db.movies.aggregate( [ { $match: { rated: "PG", released: { $gt: ISODate("2000-01-01T00:00:00Z") } } }, { $sort: { title: 1 } }, { $limit: 5 }, { $project: { _id: 1, "imdb.rating": 1 } } ] )
インデックスとクエリは ESR ルールに従います。
ratedは等価一致(E)に使用されるため、インデックスの最初のフィールドになります。titleはソート(S)に使用されるため、インデックスのrated後にあります。releasedは範囲クエリ(R )に使用されるため、インデックスの最後のフィールドになります。
クエリ結果を制限する
MongoDB カーソル は結果をバッチで返します。必要な結果の数がわかっている場合は、limit() メソッドでその値を指定します。結果を制限すると、ネットワーク リソースの需要が減ります。
クエリが期待されるドキュメントを返すことを確保するため、制限を適用する前に結果をソートします。例えば、movies コレクションへのクエリから 10 件の結果のみが必要な場合は、次のクエリを実行します。
db.movies.find( {}, { title: 1, year: 1 } ).sort( { year: -1 } ).limit(10)
結果の制限の詳細については、limit() を参照してください。
インデックス ヒントの使用
クエリオプティマイザは通常、特定の操作に最適なインデックスを選択します。ただし、hint() メソッドを使用すると、MongoDB に特定のインデックスを強制的に使用させることができます。hint() を使用してパフォーマンステストをサポートするか、複数のインデックスに表示されるフィールドをクエリする場合は、MongoDB が正しいインデックスを使用するようにします。
サーバーサイド操作の使用
ドキュメント内の値を増減するには、$inc 演算子を使用します。演算子は、ドキュメントを選択し、クライアントコードで変更を加え、その後ドキュメント全体をサーバーに書き込む の代わりに、サーバー側でフィールドの値を増加します。$inc 演算子は、2 つのアプリケーションインスタンスがドキュメントをクエリし、フィールドを手動で増加させて、ドキュメント全体を同時に保存するときに発生する競合状態を回避するのにも役立ちます。
カバード クエリの実行
カバード クエリとは、インデックスを使用して完全に処理が完了し、ドキュメントを調査する必要がないクエリです。次のすべてが当てはまる場合、インデックスはクエリをカバーします。
クエリ内のすべてのフィールド(アプリケーションで指定されたフィールドと、シャーディングなどの内部で必要なフィールドの両方)はインデックスの一部です。
結果に返されるすべてのフィールドは同じインデックスにある
クエリ内のどのフィールドも
nullと等しくありません。例、次のクエリ述語ではカバード クエリになることはありません。{ "field": null }{ "field": { $eq: null } }
例
movies コレクションの rated フィールドと title フィールドには次のインデックスがあります。
db.movies.createIndex( { rated: 1, title: 1 } )
このインデックスでは、rated フィールドと title フィールドをクエリし、title フィールドのみを返す次の操作をカバーします。
db.movies.find( { rated: "PG", title: /^T/ }, { title: 1, _id: 0 } ).limit(3)
指定されたインデックスがクエリをカバーするには、インデックスに _id フィールドが含まれていないため、プロジェクション ドキュメントは _id: 0 を明示的に指定して結果から _id フィールドを除外する必要があります。
埋め込みドキュメント
インデックスは、埋め込みドキュメント内のフィールドに対するクエリをカバーできます。
例えば、次の構造のドキュメントを持つ sample_mflix データセットの theaters コレクションを考えてみましょう。
{ theaterId: <num>, location: { address: { street1: "<address>", city: "<city>", state: "<state>", zipcode: "<zip>" }, geo: { ... } } }
コレクションには、次のインデックスがあります。
db.theaters.createIndex( { "location.address.city": 1 } )
{ "location.address.city": 1 } インデックスは、次のクエリをカバーします。
db.theaters.find( { "location.address.city": "Portland" }, { "location.address.city": 1, _id: 0 } ).limit(1)
注意
埋め込みドキュメントのフィールドにインデックスを付けるには、ドット表記を使用します。「埋め込みフィールドでのインデックスを作成する」を参照してください。
マルチキーのカバーリング
マルチキー インデックスは、どのフィールドがインデックスをマルチキーにするかを追跡する場合、非配列フィールドに対するクエリをカバーできます。
マルチキー インデックスは、配列フィールドに対するクエリをサポートしていません。
マルチキー インデックスを使用したカバード クエリの例については、マルチキー インデックスのページのカバード クエリを参照してください。
パフォーマンス
インデックスにはクエリに必要なすべてのフィールドが含まれているため、MongoDB はクエリ条件を一致させることも、インデックスのみを使用して結果を返すこともできます。
インデックスのみをクエリする方が、インデックス外のドキュメントをクエリするよりもはるかに高速です。インデックス キーは通常、カタログするドキュメントよりも小さく、インデックスは通常 RAM で利用可能か、ディスク上に連続してに配置されます。
制限
インデックス タイプ
すべてのインデックス タイプがクエリをカバーできるわけではありません。対象となるインデックスのサポートの詳細については、対応するインデックス タイプのドキュメント ページを参照してください。
シャーディングされたコレクション
mongos で実行すると、インデックスにシャードキーが含まれている場合にのみ、シャーディングされたコレクションのクエリをカバーできます。
explain の結果
クエリがカバード クエリであるかどうかを判断するには、db.collection.explain()メソッドまたは explain() メソッドを使用します。「カバード クエリ」を参照してください。