このトピックでは、インデックス効率の分析、インデックスオプションの最適化、特定のクエリに対する効率的なインデックスの作成など、ApsaraDB for MongoDB でのインデックス作成のベストプラクティスについて説明します。
インデックスの選択方法
ApsaraDB for MongoDB は、複数のタイプのインデックスをサポートしています。シナリオに基づいてインデックスタイプを選択する必要があります。
単一キーインデックスの使用
ApsaraDB for MongoDB の使用において単一キーのクエリのみを実行する場合は、単一キーインデックスを作成することをお勧めします。
結合インデックスの使用
ApsaraDB for MongoDB の使用において単一キーおよび複数キーのクエリを実行する場合は、最大 32 個のキーの組み合わせをサポートする結合インデックスを作成することをお勧めします。たとえば、次のコマンドを実行して、category フィールドと item フィールドを含む結合インデックスを作成できます。
db.products.createIndex( { "category": 1, "item": 1 } )テキストインデックスの使用
従来のインデックスは、フィールドの値を照合するために使用されます。大量のテキストを含むフィールド内の特定の単語のみを照合したい場合は、テキスト照合のためにテキストインデックスを使用する必要があります。テキストインデックスの詳細については、「自己管理型デプロイメントのテキストインデックス」をご参照ください。
インデックスの照合順序の指定
文字列比較にインデックスを使用する場合、クエリ操作に同じ照合順序を指定する必要があります。クエリ操作に異なる照合順序を指定すると、照合順序を持つインデックスはインデックスフィールドの文字列比較をサポートしません。
次の例は、文字列フィールド myColl にインデックスを持つコレクションを示しています。このインデックスは、照合順序ロケール category "fr" を使用します:
db.myColl.createIndex( { category: 1 }, { collation: { locale: "fr" } } )次のクエリ操作でインデックスを使用するには、インデックスと同じ照合順序を指定します:
db.myColl.find( { category: "cafe" } ).collation( { locale: "fr" } )次のクエリ操作は、デフォルトの "simple" バイナリ照合順序を使用するため、インデックスを使用できません:
db.myColl.find( { category: "cafe" } )インデックスプレフィックスキーが文字列、配列、または埋め込みドキュメントではない結合インデックスの場合、異なる照合順序を指定したクエリ操作でも、インデックスを使用してインデックスプレifックスキーの比較をサポートできます。照合順序の詳細については、「照合順序のロケールとデフォルトパラメーター」をご参照ください。
スロークエリログに基づくインデックスの分析
ApsaraDB for MongoDB はインデックスを最適化して、スキャンされるコレクションの数を減らします。したがって、スロークエリログの DocsExamined メトリックと KeysExamined メトリックに注目する必要があります。スロークエリログの表示方法の詳細については、「スロークエリログの表示」をご参照ください。
DocsExamined: クエリリクエストに対してスキャンされたドキュメントの数。メトリック値が大きい場合は、インデックス以外の多くのエントリをスキャンする必要があることを示します。この場合、スキャンされたドキュメントが多いフィールドにインデックスを作成することをお勧めします。
KeysExamined: 特定のインデックスでスキャンされたキーの数。メトリック値は大きいが返された nreturned 値が小さい場合、指定されたクエリ条件を満たすデータを取得するために多くのインデックスキーがスキャンされます。これはインデックスが非効率であることを示します。この場合、インデックスを調整するか、別のインデックスを作成する必要があります。
次のインデックス分析ロジックが適用されます:
コレクション全体のスキャン (キーワード: COLLSCAN および DocsExamined)
COLLSCAN はコレクション全体のスキャンを示します。クエリ、更新、削除などの操作を実行した後、低速なリクエストログを表示すると COLLSCAN キーワードが見つかります。クエリしたいフィールドにインデックスを作成することをお勧めします。
DocsExamined フィールドは、クエリリクエストに対してスキャンされたドキュメントの数を示します。フィールド値が大きいほど、このリクエストによって占有される CPU リソースが高くなります。
不適切なインデックス (キーワード: IXSCAN および keysExamined)
keysExamined フィールドは、インデックスを使用するリクエストに対してスキャンされたインデックスキーの数を示します。フィールド値が大きいほど、このリクエストによって占有される CPU リソースが高くなります。
不適切であったり、大量のデータに一致するインデックスを作成した場合、そのインデックスは CPU オーバーヘッドを削減したり、リクエストの実行を高速化したりすることはできません。
スロークエリログに SORT キーワードが見つかった場合は、インデックスを使用してソートパフォーマンスを最適化できます。詳細については、「ESR (等価、ソート、範囲) ルール」をご参照ください。
インデックスの最適化方法
カバードクエリの使用
カバードクエリは、ソースドキュメントにアクセスせずにインデックスから直接結果を返します。これは非常に効率的です。クエリがカバードクエリであるかどうかを判断するには、explain() コマンドを使用します。explain() の出力で totalDocsExamined が 0 と表示されている場合、そのクエリはインデックスによってカバーされています。
explain() の出力に totalDocsExamined フィールドが含まれていない場合は、クエリを executionStats または allPlansExecution モードで実行します。たとえば、explain("executionStats") または explain("allPlansExecution") を使用します。
カバードクエリを実装しようとすると、_id フィールドはデフォルトで常に返されます。これは一般的な落とし穴です。クエリ結果から明示的に除外するか、インデックスに追加する必要があります。
シャードクラスターでは、MongoDB は内部的にシャーディングキーフィールドにアクセスする必要があります。クエリがカバーされるためには、シャーディングキーがインデックスの一部である必要があります。したがって、インデックスにシャーディングキーを含めることがベストプラクティスです。
冗長なインデックスの削除
インデックスはリソースを大量に消費します。ApsaraDB for MongoDB の WiredTiger ストレージエンジンで圧縮が使用されている場合でも、インデックスは RAM とディスクリソースを消費する可能性があります。さらに、フィールドが更新されると、関連するインデックスも維持する必要があり、これにより追加の CPU およびディスク I/O 負荷が発生します。したがって、不要になったインデックスは慎重に評価して削除することをお勧めします。
結合インデックスの推奨
複数のフィールドに対する結合クエリの場合、クエリ内のフィールドの順序は問題になりません。必要なインデックスは 1 つだけです。たとえば、フィールド `a` と `b` に対するクエリの場合、インデックス
{a:1, b:1}または{b:1, a:1}のいずれか 1 つだけが必要です。冗長なインデックスは、包含関係によって引き起こされる可能性があります。たとえば、次のクエリが使用されます:
db.myCol.find({"b": 2, "c": 3})db.myCol.find({"a": 1, "b": 2, "c": 3})
2 番目のクエリには、最初のクエリのすべてのフィールドが含まれています。単一のインデックスを使用して両方のクエリを満たすことができます。これを行うには、より具体的なクエリのフィールドをインデックスの先頭に配置します。インデックスは
{b: 1, c: 1, a: 1}になります。一意なインデックスと他のフィールドの組み合わせによって引き起こされる冗長なインデックス。たとえば、次のクエリが使用されます:
db.myCol.find({"a": 1, "b": 1})db.myCol.find({"a": 1, "c": 1})
`a` フィールドに一意の値がある場合、`a` に加えて他のフィールドに結合インデックスを作成しても、これらのクエリには役立ちません。
{a: 1}にインデックスを作成するだけで十分です。
非等価インデックスの推奨
一部のクエリに対しては、非等価な複合クエリインデックスを作成しないでください。例:
db.myCol.find({"a": {$gte: 1} , "b": {$lte: 1}})複数のフィールドを含むこの非等価クエリでは、左端のフィールドのみがインデックス付けできます。これは、a フィールドのみがインデックス付けされることを示します。a フィールドのインデックスを作成するだけで十分です。
等価クエリと非等価クエリの組み合わせを使用できます。例:
db.myCol.find({"a": {$gte: 1} , "b": 1})この場合、最適なインデックスは等価クエリフィールドを最初に持つ必要があります。インデックス
{b: 1, a: 1}を作成する必要があります。
$or 型クエリインデックスの推奨
$or 型のクエリでは、各条件に対してインデックスを作成する必要があります。例:
db.myCol.find({$or: [{"a": 1, "b": 1}, {"c": 1, "d": 1}]})
$or クエリの各句に対して最適なインデックスを作成する必要があります。クエリ {$or: [{"a": 1, "b": 1}, {"c": 1, "d": 1}]} の場合、{a: 1, b: 1, c: 1, d: 1} のようにすべてのフィールドを含む単一の結合インデックスではなく、{a: 1, b: 1} と {c: 1, d: 1} の 2 つの個別のインデックスを作成する必要があります。
ソートクエリインデックスの推奨
同じフィールドを含む異なるソートクエリに対しては、インデックスを 1 つ作成するだけで十分です。例:
db.myCol.find({}).sort({"a":1})db.myCol.find({}).sort({"a":-1})
インデックス
{a: 1}を作成するだけで十分です。複数フィールドのソートクエリを使用できます。例:
db.myCol.find({}).sort({"a":1, "b": -1})インデックス
{a: 1, b: 1}はこのクエリには効果がありません。インデックス{a: 1, b: -1}を作成する必要があります。等価、非等価、およびソートクエリの組み合わせを使用できます。例:
db.myCol.find({"a": 1, "b": 2, "c": {$gte: 1}}).sort({"d": 1, "e": -1})インデックス内のフィールドの順序は
等価->ソート->非等価である必要があります。たとえば、インデックスは{a: 1, b: 1, d: 1, e: -1, c: 1}です。$or 型クエリとソートクエリの組み合わせを使用できます。例:
db.myCol.find({$or: [{"a": 1, "b": 1}, {"c": 1, "d": 1}]}).sort({"e": -1})このクエリは 2 つのクエリに分割できます:
db.myCol.find({"a": 1, "b": 1}).sort({"e":-1})とdb.myCol.find({"c": 1, "d": 1}).sort({"e":-1})。等価クエリとソートクエリを組み合わせるルールに従い、インデックス{a: 1, b: 1, e: -1}と{c: 1, d: 1, e: -1}を作成する必要があります。
マッピングを使用して必要なフィールドのみを取得する
ドキュメント内の一部のフィールドのみが必要な場合は、マッピングを使用して必要なフィールドのみを取得することで、パフォーマンスを向上させることができます。
たとえば、posts コレクションのクエリで timestamp、title、author、abstract フィールドのみが必要な場合は、次のクエリコマンドを実行できます:
db.posts.find( {}, { timestamp : 1 , title : 1 , author : 1 , abstract : 1} ).sort( { timestamp : -1 } )
hint() を使用して特定のインデックスを取得する
ほとんどの場合、クエリ オプティマイザーは操作に最適なインデックスを選択します。ただし、hint() メソッドを使用して MongoDB に特定のインデックスを強制的に使用させることもできます。
たとえば、パフォーマンステストや、複数のインデックスに含まれるフィールドを選択する必要があるクエリに hint() を使用できます。
部分インデックスの使用
部分インデックスを使用すると、インデックスのサイズとパフォーマンスのオーバーヘッドを削減できます。つまり、作成されたインデックスにはクエリ可能なフィールドのみが含まれます。
たとえば、コレクションにフィールド a, b, c が含まれているとします。クエリ条件に a フィールドのみが含まれる場合は、a フィールドにのみインデックスを作成します。