MongoDB インデックスの作成

MongoDBBeginner
オンラインで実践に進む

はじめに

この実験 (Lab) では、MongoDB でインデックスを作成および管理するための基本的なテクニックを学びます。インデックスは、コレクション内のすべてのドキュメントをスキャンするよりもはるかに高速にデータを検索および取得できるため、データベースのパフォーマンスを最適化する上で非常に重要です。単一フィールドインデックス、複合インデックス、およびユニークインデックスの作成、クエリパフォーマンスへの影響の分析、およびライフサイクルの管理を実践します。この実験 (Lab) の終わりには、MongoDB クエリをより効率的にするためのインデックスの使用方法について、確かな理解が得られるでしょう。

単一フィールドインデックスの作成

最初のステップでは、MongoDB に接続し、サンプルコレクションを持つデータベースを作成してから、単一フィールドに最初のインデックスを作成します。単一フィールドインデックスは、特定のフィールドでフィルタリングまたはソートするクエリのパフォーマンスを向上させます。

まず、ターミナルで mongosh と入力して MongoDB Shell を開きます。これにより、環境で実行されている MongoDB サーバーに接続されます。

mongosh

MongoDB Shell に入ると、> プロンプトが表示されます。indexlab という新しいデータベースを作成して切り替えましょう。特に指定がない限り、この実験 (Lab) での以降のすべてのコマンドはこのシェル内で実行されます。

use indexlab

次に、users という名前の新しいコレクションにサンプルドキュメントを挿入しましょう。このデータは、実験 (Lab) 全体で使用されます。

db.users.insertMany([
  { name: "Alice", age: 28, email: "alice@example.com" },
  { name: "Bob", age: 35, email: "bob@example.com" },
  { name: "Charlie", age: 42, email: "charlie@example.com" }
]);

データが準備できたので、name フィールドにインデックスを作成しましょう。単一フィールドのインデックスは、そのフィールドでドキュメントを検索するクエリを高速化するのに役立ちます。

db.users.createIndex({ name: 1 });

上記のコマンドでは、{ name: 1 } は、name フィールドに昇順でインデックスを作成することを指定しています。-1 を使用すると降順インデックスが作成されます。

インデックスが作成されたことを確認するには、users コレクションのすべてのインデックスを一覧表示できます。

db.users.getIndexes();

出力には 2 つのインデックスが表示されるはずです。1 つは MongoDB がすべてのコレクションに作成するデフォルトの _id インデックスで、もう 1 つは作成したばかりの name_1 インデックスです。

[
  { "v": 2, "key": { "_id": 1 }, "name": "_id_" },
  { "v": 2, "key": { "name": 1 }, "name": "name_1" }
]

複合インデックスの構築

単一フィールドインデックスは便利ですが、多くのクエリは複数のフィールドでフィルタリングします。これらのケースでは、複数のフィールドを含む複合インデックスがパフォーマンスを大幅に向上させることができます。このステップでは、複合インデックスを作成します。

mongosh シェルで続行しましょう。age および name フィールドに複合インデックスを作成します。複合インデックス内のフィールドの順序は重要です。MongoDB は、このインデックスを使用して、age のみ、または agename の両方でフィルタリングするクエリをサポートできます。

db.users.createIndex({ age: -1, name: 1 });

このコマンドは、ドキュメントを age の降順 (-1) で最初にソートし、同じ年齢のドキュメントについては name の昇順 (1) でソートするインデックスを作成します。

クエリのためにコレクションをより多様にするために、さらにいくつかのドキュメントを追加しましょう。

db.users.insertMany([
  { name: "David", age: 28, email: "david@example.com" },
  { name: "Eve", age: 35, email: "eve@example.com" }
]);

これで、新しい複合インデックスを確認するために、再度インデックスのリストを表示しましょう。

db.users.getIndexes();

出力には、以前のものに加えて、age_-1_name_1 インデックスも含まれるようになります。

[
  { "v": 2, "key": { "_id": 1 }, "name": "_id_" },
  { "v": 2, "key": { "name": 1 }, "name": "name_1" },
  { "v": 2, "key": { "age": -1, "name": 1 }, "name": "age_-1_name_1" }
]

この複合インデックスは、age でフィルタリングまたはソートするクエリ、あるいは age の後に name でフィルタリングまたはソートするクエリを効率的に処理します。

ユニークインデックスの作成

インデックスは、データ整合性を強制するためにも使用できます。ユニークインデックスは、インデックスが付けられたフィールド(またはフィールド群)に重複する値が含まれていないことを保証します。このステップでは、users コレクションで重複するメールアドレスを防ぐためにユニークインデックスを作成します。

email フィールドにユニークインデックスを作成しましょう。これは、インデックス作成時に { unique: true } オプションを追加することで行います。

db.users.createIndex({ email: 1 }, { unique: true });

ユニークインデックスが設定されたので、MongoDB は、email フィールドの重複する値が発生するようなドキュメントの挿入または更新の試みを拒否します。

これをテストしてみましょう。まず、新しいユニークなメールアドレスを持つドキュメントを挿入してみてください。これは成功するはずです。

db.users.insertOne({ name: "Frank", age: 31, email: "frank@example.com" });

次に、既存のメールアドレス(例:alice@example.com)を持つ別のドキュメントを挿入しようとします。この操作は失敗します。try...catch ブロックを使用すると、mongosh シェルから切断されることなくエラーを確認できます。

try {
  db.users.insertOne({ name: "Fiona", age: 29, email: "alice@example.com" });
} catch (e) {
  print(e);
}

コマンドは、重複キー違反を示すエラーをスローします。出力には E11000 duplicate key error collection のようなメッセージが含まれます。

ユニーク制約プロパティを確認するために、再度インデックスを表示できます。

db.users.getIndexes();

出力の email_1 インデックスにある unique: true プロパティに注目してください。

[
  ...,
  {
    v: 2,
    key: { email: 1 },
    name: 'email_1',
    unique: true
  }
]

explain() を使用したインデックス使用状況の分析

インデックスの作成は戦いの半分に過ぎません。MongoDB がクエリで実際にそれらを使用していることを確認する必要もあります。explain() メソッドは、クエリの実行方法に関する詳細情報を提供する、このための強力なツールです。このステップでは、explain() を使用して MongoDB が既存のインデックスを効果的に使用していることを確認する方法を示します。

特定の年齢のユーザーを見つけるクエリを分析してみましょう。前のステップで既に複合インデックス age_-1_name_1 を持っているため、MongoDB はこのインデックスを使用して age フィールドに対するクエリを最適化できます。

db.users.find({ age: 35 }).explain("executionStats");

出力では、winningPlan 内の executionStats.stage フィールドを探してください。IXSCAN("Index Scan" の略)という値が表示されるはずです。これは、MongoDB が既存の複合インデックス age_-1_name_1 を使用して関連ドキュメントを迅速に見つけていることを示しています。また、totalDocsExamined が返されたドキュメント数と一致していることも確認できるはずです。これは、複合インデックスを使用することの効率性を示しています。

MongoDB がどのようにインデックスを選択するかをよりよく理解するために、以前に作成した単一フィールド name インデックスから恩恵を受けることができるクエリもテストしてみましょう。

db.users.find({ name: "Alice" }).explain("executionStats");

このクエリでも、winning plan stage として IXSCAN が表示されるはずです。これは、MongoDB が最初のステップで作成した name_1 インデックスを使用していることを確認します。

インデックスの表示と削除

インデックス管理の最後の部分は、それらをリストする方法と、不要になったときに削除する方法を知ることです。未使用のインデックスはストレージを消費し続け、書き込み操作にオーバーヘッドを追加するため、クリーンアップすることが良い習慣です。

まず、users コレクションでこれまでに作成したすべてのインデックスの完全なリストを取得しましょう。

db.users.getIndexes();

このコマンドは、現在のインデックス設定の包括的な概要を提供します。例えば、複合インデックス age_-1_name_1 がもはや不要であると判断したとします。インデックス名を指定して dropIndex() メソッドを使用して削除できます。

db.users.dropIndex("age_-1_name_1");

コマンドを実行すると、確認メッセージが表示されます。確実にするために、もう一度インデックスをリストして、削除されたことを確認できます。

db.users.getIndexes();

age_-1_name_1 インデックスはリストに表示されなくなっているはずです。

コレクションからすべてのカスタムインデックス(デフォルトの _id インデックスを除く)を削除する必要がある場合は、dropIndexes() メソッドを使用できます。このコマンドは強力なので、注意して使用してください。

// 例:db.users.dropIndexes()

これで、MongoDB でのインデックス管理の基本的な操作は終了です。これで mongosh シェルを終了できます。

exit;

まとめ

この実験では、MongoDB のインデックスを扱うための基本的なスキルを学びました。まず、単純なクエリを高速化するための基本的な単一フィールドインデックスを作成しました。次に、複数のフィールドを含むクエリを最適化するための複合インデックスの構築に進みました。また、ユニークインデックスを作成してデータ整合性を強制する方法も学びました。さらに、explain() メソッドを使用してクエリプランを分析し、インデックスが効果的に使用されていることを確認し、コレクションスキャンとインデックススキャンのパフォーマンスの違いを観察しました。最後に、インデックスをリストおよび削除して管理する練習をしました。これらのインデックス作成技術を習得することは、MongoDB で高速かつスケーラブルなアプリケーションを構築するための重要なステップです。