運用上の要因とデータモデル
MongoDB のアプリケーション データのモデル化では、MongoDB のパフォーマンスに影響を与えるさまざまな運用上の要因を考慮する必要があります。 たとえば、異なるデータモデルは、より効率的なクエリ、挿入および更新操作のスループットの増加、またはシャーディングされたクラスターへのアクティビティのより効果的な分散を可能にします。
データモデルを開発する際は、次の考慮事項と組み合わせて、アプリケーションのすべての読み取りおよび書込み操作を分析します。
アトミック性
MongoDB での書込み操作は、単一のドキュメント内の 複数の埋め込みドキュメントを変更する場合でも、単一のドキュメント レベルで アトミック です。 単一の書込み操作が複数のドキュメントを変更する場合(例: db.collection.updateMany()
)、各ドキュメントの変更はアトミックですが、操作全体はアトミックではありません。
埋め込みデータモデル
埋め込みデータは、複数のドキュメントやコレクションにわたって正規化するのではなく、関連するすべてのデータを 1 つのドキュメントにモデル化します。 このデータモデルはアトミック操作を容易にします。
単一のドキュメントに対してアトミックなアップデートを提供するサンプルデータモデルについては、 「 アトミック操作のモデルデータ」を参照してください。
マルチドキュメントトランザクション
関連するデータ間の参照を保存するデータモデルの場合、アプリケーションはこれらの関連データを検索および変更するために、個別の読み取り操作と書込み操作を発行する必要があります。
MongoDB は(単一または複数のコレクション内の)複数のドキュメントへの読み取りと書込みにアトミック性を必要とする状況で、レプリカセットやシャーディングされたクラスターでのトランザクションを含む分散トランザクションをサポートします。
詳細についてはトランザクションを参照してください。
重要
ほとんどの場合、分散トランザクションでは 1 つのドキュメントの書き込み (write) よりもパフォーマンス コストが高くなります。分散トランザクションの可用性は、効果的なスキーマ設計の代わりにはなりません。多くのシナリオにおいて、非正規化されたデータモデル(埋め込みドキュメントと配列)が引き続きデータやユースケースに最適です。つまり、多くのシナリオにおいて、データを適切にモデリングすることで、分散トランザクションの必要性を最小限に抑えることができます。
トランザクションの使用に関するその他の考慮事項(ランタイム制限や oplog サイズ制限など)については、「本番環境での考慮事項」も参照してください。
シャーディング
MongoDB はシャーディングを使用して水平スケーリングを提供します。 これらのクラスターは、大規模なデータセットと高スループット操作を伴う配置をサポートします。 シャーディングにより、ユーザーはデータベース内の コレクション を 分割mongod
して、コレクションのドキュメントを多数の インスタンスまたは シャードに分散できます。
シャーディングされたコレクションでデータとアプリケーションのトラフィックを分散するために、MongoDB はシャードキーを使用します。 適切なシャードキーを選択することはパフォーマンスに大きな影響を与え、クエリの分離や書込み容量の増加を可能または阻止します。 シャードキーは後で変更できますが、シャードキーの選択を慎重にすることが重要になります。
Indexes
一般的なクエリのパフォーマンスを向上させるには、 インデックス を使用します。 ソートされた結果を返すクエリやすべての操作で頻繁に表示されるフィールドにインデックスをビルドします。 MongoDB は_id
フィールドに一意のインデックスを自動的に作成します。
インデックスを作成する際は、次のインデックスの動作を考慮してください。
各インデックスには少なくとも8 kB のデータ領域が必要です。
インデックスを追加すると、書き込み操作のパフォーマンスに悪影響が生じます。 書き込みと読み取りの比率が高いコレクションでは、挿入のたびにすべてのインデックスもアップデートする必要があるため、インデックスは高価になります。
読み取りと書込みの比率が高いコレクションでは、多くの場合、追加のインデックスのメリットが得られます。 インデックスは、インデックスがない読み取り操作には影響しません。
アクティブにすると、各インデックスはディスク領域とメモリを消費します。 この使用量は重要な可能性があり、特にワーキングセットのサイズに関する懸念事項については、キャパシティー プランニングで追跡する必要があります。
インデックスの詳細と、 説明計画結果の解釈 について は、「 インデックス作成戦略 」を参照してください。さらに、MongoDBデータベースプロファイラーは非効率的なクエリを識別するのに役立つ場合があります。
多数のコレクション
状況によっては、関連情報を 1 つのコレクションではなく複数のコレクションに保存することもあります。
さまざまな環境とアプリケーションのログドキュメントを保存するサンプル コレクションlogs
について考えてみましょう。 logs
コレクションには次の形式のドキュメントが含まれています。
{ log: "dev", ts: ..., info: ... } { log: "debug", ts: ..., info: ...}
ドキュメントの合計数が少ない場合は、ドキュメントをタイプ別にコレクションにグループ化できます。 ログについては、 logs_dev
やlogs_debug
などの個別のログ コレクションの保持を検討してください。 logs_dev
コレクションには、開発環境に関連するドキュメントのみが含まれます。
一般的に、コレクションの数が多い場合でもパフォーマンス上の大きな低下はなく、パフォーマンスが非常に向上します。 個別のコレクションは、高スループットのバッチ処理にとって非常に重要です。
多数のコレクションを持つモデルを使用する場合は、次の動作を考慮してください。
コレクションには多数の小さなドキュメントが含まれています
多数の小さなドキュメントを含むコレクションがある場合は、パフォーマンス上の理由で埋め込みを検討する必要があります。 これらの小さなドキュメントを何らかの論理的な関係でグループ化し、このグループ化でドキュメントを頻繁に検索する場合は、埋め込みドキュメントの配列を含む大きなドキュメントに、小さなドキュメントを「ロールアップ」することを検討してください。
これらの小さなドキュメントを論理グループに「ロールアップ」するということは、ドキュメントのグループを取得するためのクエリに含まれる順次読み取りで、ランダムなディスク アクセスが少なくなります。 さらに、ドキュメントを「ロールアップ」し、共通フィールドを大きなドキュメントに移動することは、これらのフィールドのインデックスにメリットをもたらす。 共通フィールドのコピーが少なくなり、対応するインデックス内の関連するキーエントリも少なくなります。 インデックスの詳細については、「 インデックス 」を参照してください。
ただし、グループ内のドキュメントのサブセットのみを取得する必要が頻繁にある場合は、ドキュメントを「ロールアップ」してもパフォーマンスが向上しない可能性があります。 さらに、小さくて個別のドキュメントがデータの自然モデルを表す場合は、そのモデルを維持する必要があります。
小さいドキュメントのストレージの最適化
各 MongoDB ドキュメントには一定量のオーバーヘッドが含まれています。 このオーバーヘッドは通常は重要ではありませんが、コレクション内のドキュメントに 1 つまたは 2 つのフィールドしかない場合など、すべてのドキュメントが数バイトの場合は重要になります。
これらのコレクションのストレージ使用を最適化するための次の提案と戦略を検討してください。
_id
フィールドを明示的に使用する。MongoDB クライアントは自動的に各ドキュメントに
_id
フィールドを追加し、_id
フィールド用に一意の12バイトのObjectIdを生成します。 さらに、MongoDB は常に_id
フィールドをインデックス化します。 小さいドキュメントの場合、これはかなりの量のスペースを占める可能性があります。ストレージの使用を最適化するために、ユーザーはコレクションにドキュメントを挿入するときに明示的に
_id
フィールドの値を指定できます。 この戦略により、アプリケーションは_id
フィールドにドキュメントの別の部分を占有する値を保存できるようになります。_id
フィールドには任意の値を保存できますが、この値はコレクション内のドキュメントのプライマリキーとして機能するため、ドキュメントを一意に識別する必要があります。 フィールドの値が一意でない場合、コレクション内で競合が発生するため、プライマリキーとして機能することはできません。より短いフィールド名を使用します。
注意
フィールド名を短縮すると表現力が低下するため、ドキュメントのオーバーヘッドが大きな問題にならない場合、大きなドキュメントでは大きなメリットは得られません。 インデックスには事前定義された構造があるため、フィールド名が短くしてもインデックスのサイズが縮小されることはありません。
一般に、短いフィールド名を使用する必要はありません。
MongoDB はすべてのフィールド名を各ドキュメントに保存します。 ほとんどのドキュメントにおいて、これはドキュメントで使用されるスペースのごく一部を表します。ただし、小さいドキュメントの場合、フィールド名はそれに比例して大きな量のスペースを表す場合があります。 次のような小さなドキュメントのコレクションを考えてみましょう。
{ last_name : "Smith", best_score: 3.9 } 次のように、
last_name
という名前のフィールドをlname
に、best_score
という名前のフィールドをscore
に短縮すると、1 ドキュメントあたり9バイトを節約できます。{ lname : "Smith", score : 3.9 } ドキュメントを埋め込みます。
場合によっては、ドキュメントを他のドキュメントに埋め込み、ドキュメントごとのオーバーヘッドを節約したい場合があるかもしれません。 「 多数の小さなドキュメントが含まれているコレクションには多数の小さなドキュメントが含まれている 」を参照してください。
データライフサイクル管理
データモデリングの決定には、データライフサイクルの管理を考慮する必要があります。
コレクションのTime to Live または TTL 機能では、一定期間後にドキュメントが期限切れになります。 アプリケーションでデータベースに一定期間保持するデータが必要な場合は、TTL 機能の使用を検討してください。
さらに、アプリケーションが最近挿入されたドキュメントのみを使用する場合は、 Capped コレクションの使用を検討してください。 Capped コレクションは、挿入されたドキュメントの先入れ先出し(FIFO)管理を提供し、挿入順序に基づいてドキュメントを挿入および読み取りする操作を効率的にサポートします。