Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【ITニュース解説】MongoDB Multikey Indexes and Index Bound Optimization

2025年09月16日に「Dev.to」が公開したITニュース「MongoDB Multikey Indexes and Index Bound Optimization」について初心者にもわかりやすく解説しています。

作成日: 更新日:

ITニュース概要

MongoDBは、インデックスに配列が含まれるかを記録し、クエリを最適化する。単一の値のみのフィールドでは検索範囲を狭め効率的だが、配列を含む場合は最適化が制限される。同じ文書内の複数配列フィールドにまたがるインデックスは作成できない。柔軟なDBだが、インデックスの特性を理解して使うことが重要だ。

ITニュース解説

MongoDBでデータベースのデータを効率的に検索するためには、インデックスが非常に重要な役割を果たす。インデックスは、書籍の索引のように、特定のフィールドの値から目的のドキュメントを素早く見つける手助けをするものだ。この記事では、MongoDBがどのようにインデックスを管理し、特に配列を含むフィールド(マルチキーインデックス)がクエリのパフォーマンスにどう影響するかを解説する。

まず、field1field2という二つのフィールドにインデックスを作成した場合を考える。これらのフィールドがそれぞれ単一の値(スカラー値と呼ぶ)しか持たないドキュメントであれば、一つのドキュメントに対してインデックスには一つのエントリが作られる。このとき、MongoDBのクエリプランナーは、そのインデックスがマルチキー(配列を含む)ではないと判断する。クエリの実行計画を表示するexplainコマンドを使うと、isMultiKey: falseと表示され、multiKeyPaths: { field1: [], field2: [] }のように、どのフィールドも配列ではないことが示される。

このように、フィールドがスカラー値のみを含むことが分かっている場合、MongoDBは非常に効率的なインデックススキャンを行う。例えば、field1が1より大きく3より小さいという条件で検索した場合、クエリプランナーはインデックスの中からfield1の値が1と3の間のエントリだけを直接探し出すことができる。この厳密な範囲指定(indexBounds: { field1: [ '(1, 3)' ], field2: [ '[MinKey, MaxKey]' ] }のように、field1には明確な範囲を、field2には最小値から最大値までの全範囲を指定する)により、検索対象となるインデックスエントリの数を最小限に抑え、素早く結果を返すことが可能となる。この効率的な処理は、データベースのパフォーマンスにとって非常に有利だ。

次に、データベースに、field2が複数の値を持つ配列になっているドキュメントを追加した場合を考える。この場合、field2の配列に含まれる各値がインデックスエントリとして格納されるため、一つのドキュメントであってもインデックスには複数のエントリが作られる。このようなインデックスを「マルチキーインデックス」と呼ぶ。$unwindアグリゲーションを使うと、配列の各要素を行に展開して、インデックスエントリが内部的にどのように生成され、並べられているかを視覚的に理解できる。このとき、explainの結果を見ると、isMultiKey: trueと表示され、multiKeyPaths: { field1: [], field2: [ 'field2' ] }のように、field2が配列を含んでいるためにマルチキーインデックスになったことがわかる。

この状況でも、field1がスカラー値のままであれば、クエリプランナーはfield1に対しては効率的な範囲指定を適用できる。例えば、field1が1より大きく3より小さいという条件で検索した場合、field1の値で絞り込みを行い、その後、同じドキュメントIDを持つ重複するインデックスエントリを識別して、ドキュメントが重複して取得されないように処理する。dupsTesteddupsDroppedといった値がexplainの結果に表示されるのは、この重複排除の処理が行われていることを示している。つまり、インデックスの一部がマルチキーであっても、スカラー値のフィールドに対しては効率的な検索が可能だ。

しかし、もしfield1も配列になった場合は、インデックスの挙動がさらに複雑になる。例えば、field1[0, 5]のような配列を持つドキュメントを追加し、再びfield1が1より大きく3より小さいという条件で検索すると、クエリプランナーは先ほどのような厳密なインデックス範囲を適用できなくなる場合がある。これは、ドキュメント全体として見ればfield1の配列内に条件を満たす値(例えばfield1[0, 5]のドキュメントではfield1 > 1を満たす5)が存在しても、インデックスのエントリレベルでは、その値が他の条件(field1 < 3)を満たさないことがあるためだ。

具体的には、MongoDBはfield1が配列になったことで、インデックスの先頭フィールドに対して厳密な範囲指定を行うのが難しくなる。explainの結果を見ると、indexBounds: { field1: [ '[-inf.0, 3)' ], field2: [ '[MinKey, MaxKey]' ] }のように、field1の範囲が広がり、インデックススキャンがより多くのエントリを調べることになる。そして、実際にドキュメントを取得した後に、残りのフィルター条件(filter: { field1: { '$gt': 1 } })を適用するという、二段階の処理が必要となる。これは、インデックススキャンだけで全ての条件を効率的に絞り込めないためで、パフォーマンスに影響を与える可能性がある。このような挙動は$elemMatchといった配列内の特定要素にのみ条件を適用する演算子を使わない場合に顕著に現れる。

さらに、MongoDBはインデックスの作成において重要な制限を設けている。それは、「並列配列」のインデックス作成の禁止だ。もし、同じドキュメント内にfield1field2の両方が配列である場合(例: { _id:3, field1: [ 0,5 ] , field2: [ "x", "y", "z" ] })、これら二つの配列フィールドに対して同時に複合インデックスを作成しようとすると、エラーが発生する。MongoServerError: cannot index parallel arraysというエラーメッセージが表示される。これは、二つの配列フィールドの全ての要素の組み合わせがインデックスエントリとして登録されることになり、インデックスのサイズが非常に大きくなり、管理が困難になるためだ。この制限は、インデックスの整合性とパフォーマンスを保つために設けられている。

インデックスがマルチキーであるかどうかは、isMultiKeymultiKeyPathsといったメタデータによって管理されている。multiKeyPathsは、インデックスがマルチキーになった原因となるフィールドのパス(ドットで区切られた階層的な名前)を示す。この情報は、ドキュメントが挿入、更新、削除される際に自動的に更新され、インデックスのメタデータとして保存されるため、クエリが実行されるたびに毎回計算し直されるわけではない。

ネストされた(入れ子になった)配列がある場合も、このmultiKeyPathsの挙動は重要だ。例えば、obj.sub1obj.sub2というネストされたフィールドにインデックスを作成した場合を考える。もしobj: { sub1: [ 1, 2, 3 ], sub2: "x" }のようにsub1だけが配列であれば、multiKeyPaths: { 'obj.sub1': [ 'obj.sub1' ], 'obj.sub2': [] }となり、sub1のみがマルチキーとして認識され、両方のフィールドに対して効率的なインデックススキャンが行える。しかし、もしobj: [ { sub1: [ 1, 2, 3 ], sub2: "x" } ]のように、親であるobj自体が配列である場合、obj.sub1obj.sub2の両方がマルチキーとして認識される。具体的には、multiKeyPaths: { 'obj.sub1': [ 'obj', 'obj.sub1' ], 'obj.sub2': [ 'obj' ] }のように、親のobjが配列であるという情報がサブフィールドにも伝播する。この場合、obj.sub2が実際にはスカラー値しか持たないとしても、親が配列であるため、クエリプランナーはobj.sub2に対して厳密なインデックス範囲を適用できず、indexBounds[MinKey, MaxKey]のように広い範囲になってしまう。

このように、MongoDBの柔軟なスキーマは、フィールドに単一の値や配列を自由に混在させることができる大きなメリットがあるが、インデックスの挙動には注意が必要だ。MongoDBのストレージエンジンは、どのインデックス付きフィールドが配列を含むかを常に追跡し、その情報をmultiKeyPathsとして記録している。クエリプランナーは、このメタデータを活用してインデックス範囲スキャンを最適化しようとするが、フィールドが配列であるか、あるいは配列の親を持つかによって、最適化の度合いが変わる。システムエンジニアを目指す上では、このインデックスの「マルチキー」という特性を理解し、クエリのパフォーマンスにどのように影響するかを把握しておくことが、効率的なデータベース設計と運用に繋がる重要な知識となるだろう。

関連コンテンツ