MongoDB の柔軟なデータモデル、データの一貫性と整合性を確保しつつ、パフォーマンスと整合性の戦略的なバランスを取ることができます。 のスキーマの計画 に関する一般的なガイダンスに加えて、データモデルを最適化し、どのスキーマ設計パターンがアプリケーションのユースケースに最適であるかを判断するための次のベストプラクティスを考慮してください。
スキーマの早期計画と反復処理
スキーマの計画と設計は、アプリケーション開発プロセスの早い段階で行う必要があります。適切なデータ モデリング プラクティスでアプリケーションを起動することで、アプリケーションが大きくなるにつれてパフォーマンスの問題が生じるのを防ぐことができます。データ モデリングのベストプラクティスに早期かつ適切に従うと、パフォーマンスが向上し、将来のアプリケーションの増やすが容易になります。
注意
アプリケーションと最適化の重要性によっては、最適化に時間を費やす前に、基本機能をカバーするシンプルで動作するデータモデルを確立することを優先したい場合があります。
データモデルを変更する
アプリケーションのニーズが変化するにつれて、スキーマを反復的に設計 し、スキーマを変更 する必要があります。 MongoDB の は、ダウンタイムなしでスキーマをシームレスに変更する方法を提供します。ただし、本番環境で使用される大規模なスキーマを変更するのは依然として困難です。
次の例で考えてみます。
コースとそのレッスンに関する情報を保存するオンライン ユーザー学習プラットフォームのバックエンドを構築するプロジェクトが与えられています。最初は、プラットフォームは各コースについて次の基本情報を保存する必要がありました。
コース名
インストラクター
説明
レッスン。各コースドキュメント内にサブドキュメントの配列として埋め込まれます。この時点で、各レッスン サブドキュメントにはタイトル、説明、プレゼンテーション スライドのみが含まれています。
プラットフォームが進化するにつれて、各コースにはビデオ、クイズ、割り当て、外部リソースリンクなど、追加の学習とテスト形式が必要になります。この新しいデータをすべて各コースドキュメントに埋め込むと、データモデルが非常に複雑になります。
代わりに、個別のlessons コレクションを作成することを検討してください。このように、参照ID を使用して、course lessonsコレクションと コレクションをリンクできます。参照を使用することで、各レッスンに異なるコンテンツタイプを含めることができ、将来の形式変更に対してより柔軟になります。
リンク関連データ
MongoDB でデータモデルを設計するときは、ドキュメントの構造と、アプリケーションが関連エンティティのデータを使う方法を考慮しましょう。
関連データをリンクするには、次のいずれかを行います。
単一のドキュメント内に関連データを埋め込みます。
別のコレクションに保存されている参照関連データ。
埋め込みや参照をいつ使用するかについての例えについては、次の表を参照してください。
Scenario | リンク方法 |
|---|---|
関連データをまとめて保持すると、データモデルとコードがより簡素化されます。 | 埋め込み |
エンティティ間に「has-a」または「contains」の関係場合。 | 埋め込み |
アプリケーションが情報をまとめてクエリします。 | 埋め込み |
一緒に更新されることが多いデータがある場合。 | 埋め込み |
同時にアーカイブする必要があるデータがある場合。 | 埋め込み |
関係の子側は濃度が高いです。 | 参照 |
データ重複は管理が複雑すぎるため、推奨されません。 | 参照 |
データの合計サイズが、アプリケーションのメモリまたは転送帯域幅をある程度必要とします。 | 参照 |
埋め込みデータは、無制限に拡張されます。 | 参照 |
データは、 書き込み負荷の高いワークロードで、異なる時間に書き込まれます。 | 参照 |
関係の子側の場合、データは親なしで単独で存在できます。 | 参照 |
各データリンク方法のユースケース、パフォーマンスに関する考慮事項、メリットの詳細については、以下を参照してください。
Duplicate Data
単一のドキュメントに関連データを埋め込む場合、2 つのコレクション間でデータを複製できます。データを重複させることにより、アプリケーションは、モデル内のエンティティを論理的に分離しながら、1 つのクエリで複数のエンティティについての関連情報をクエリできます。
データを複製する前に、以下の要素を考慮してください。
データが重複している場合の読み取りのパフォーマンス上の利点。 データを重複させることで、複数のコレクションにわたって結合を実行する必要がなくなるため、アプリケーションのパフォーマンスが向上します。
重複したデータをどのくらいの頻度でアップデートする必要があるか。頻度の低い更新を処理するために必要な追加のロジックは、読み取り操作で結合(lookup)を実行するよりもコストが低くなります。ただし、重複データを頻繁に更新すると、過剰なワークロードとパフォーマンスの問題が発生する可能性があります。
次の表は、スキーマに存在する可能性のあるさまざまなタイプの重複データを示しています。
重複データのタイプ | 説明 | 例 |
|---|---|---|
不変 | 変更されないデータ。不変データはデータ重複の適切な候補です | ユーザー アカウントが作成された日付。 |
一時的な | 時間の経過とともに変化する可能性があるが、データの履歴値を保持することが重要なデータ。 | 注文した時点でのカスタマーの住所。カスタマーが新しい住所に移動した場合、以前の注文が出荷された住所に記録されているアドレスには影響しません。 |
古さを区別する | すべてのデータ発生の一貫性を確保するために頻繁に更新を必要とするデータ。アプリケーションはトランザクションまたは trigger を使用してすべての発生を更新できます。 | 特定の製品の在庫数。カスタマーが製品を注文するたびに更新する必要があります。 |
古さを区別しない | 古くなった状態をより長く許容できるデータ。アプリケーションは、バックグラウンドジョブを使用して、許容される古さと更新コストに基づいて、データのすべての発生を定期的に更新できます。 | カスタマーに推奨される製品のリストです。新しい製品がシステムに追加されるたびに再計算する必要はありません。 |
重複したデータを頻繁にアップデートする必要がない場合は、2 つのコレクションの整合性を保つために必要な追加作業は最小限に抑えられます。ただし、重複したデータが頻繁にアップデートされる場合は、参照を使用して関連データをリンクする方がよい場合があります。
関連データを重複させると、データモデルが最適化される仕組みの例については、「 重複データの処理 」を参照してください。
データ整合性の強制
スキーマでデータを重複させる場合は、複数のコレクション間でデータの一貫性を保つ方法を決定する必要があります。例、 eコマースプラットフォームでは、製品在庫のリアルタイムステータスを提供するために継続的な最新データが必要になる可能性が高くなります。一方、ソーシャルメディア分析など、長期的な戦略的決定のためのデータを取り扱うアプリケーションは、少し古いデータを読み込むことを許容します。
次のいずれかの方法を使用して、アプリケーションにデータの整合性を強制できます。
各データ整合性の適用方法、ユースケース、およびパフォーマンスのトレードオフの詳細については、「 データの整合性 」を参照してください。
検証ルールでスキーマを強制する
スキーマ検証ニーズは、アプリケーションの使用方法によって異なります。 MongoDB の柔軟なスキーマにより、特に開発の初期段階でデータモデルを簡単に高度化 できます。ただし、データモデルが安定するにつれて、スキーマ検証 は、データが意図したとおりに表示されることを確認するのに役立つ方法になる可能性があります。スキーマ検証機能は、アプリケーションが定着して、ユーザーがデータの整理方法を明確に把握するようになったアプリケーションで最も役に立ちます。
注意
スキーマ検証ルールも柔軟であるため、アプリケーションでその必要がある場合を除き、ドキュメント内のすべてのフィールドをカバーする必要はありません。
たとえば、次の状況でスキーマ検証を利用できます。
eventsコレクションの場合、接続アプリケーションが予期しない型を使用しないように、start_dateフィールドが string ではなく日付としてのみ保存されることを確認します。storeコレクションの場合、accepted_credit_cardsフィールドが、店舗が受け入れるクレジットカードのリスト(["Visa", "MasterCard", "American Express"]など)に属していることを確認します。この検証により、ユーザーがサポート対象外のクレジットカード値を入力するのを防止します。学生コレクションでは、
gpaフィールドが常に正の浮動点数であることを確認します。この検証により、データ入力時のエラーを防止できます。
詳細については、 「 スキーマバリデーション 」を参照してください。
よくクエリされるフィールドのインデックス
データモデルを設計するときは、データへのアクセス方法と保存方法について考えてみましょう。特定のフィールドを頻繁にクエリ、フィルタリング、ソート、結合する場合は、それらのフィールドにインデックスを作成することを検討してください。インデックスを使用すると、 MongoDB次のことが可能になります。
クエリ結果をより速く返す
結果をより効率的に並べ替える
$lookup操作と$group操作を最適化するCPU と I/O 使用量の削減
アプリケーションが大きくなるにつれて、 配置のインデックスの使用状況を監視 して、インデックスが、関連するクエリを引き続きサポートしていることを確認してください。
インデックスを作成するときは、次のインデックスの動作 を考慮してください。
各インデックスには少なくとも8 kB のデータ領域が必要です。
インデックスを追加すると、書き込み操作のパフォーマンスに悪影響が生じます。 書き込みと読み取りの比率が高いコレクションでは、挿入のたびにすべてのインデックスもアップデートする必要があるため、インデックスは高価になります。
読み取りと書込みの比率が高いコレクションでは、多くの場合、追加のインデックスのメリットが得られます。 インデックスは、インデックスがない読み取り操作には影響しません。
アクティブにすると、各インデックスはディスク領域とメモリを消費します。 この使用量は重要な可能性があり、特にワーキングセットのサイズに関する懸念事項については、キャパシティー プランニングで追跡する必要があります。
インデックスの詳細については、「 インデックス作成戦略 」を参照してください。
その他の考慮事項
データモデルを開発するときは、次の考慮事項に組み合わせて、アプリケーションのすべての読み取りおよび書込み操作を分析します。
アトミック性
MongoDBでの書込み操作は、単一のドキュメント内の 複数の埋め込みドキュメントを操作する場合でも、単一のドキュメントレベルで アトミック です。つまり、アップデート操作が複数のサブドキュメントに影響する場合、それらのサブドキュメントがすべてアップデートされるか、操作が完全に失敗してアップデートが行われないかのどちらかになります。
埋め込みドキュメントと配列を使用する非正規化データモデルは、複数のドキュメントやコレクションにわたって正規化するのではなく、関連するすべてのデータを 1 つのドキュメントに組み合わせます。このデータモデルでは、操作が複数のドキュメントとコレクションに影響する正規化されたモデルとは対照的に、アトミック操作が可能です。単一のドキュメントのアトミックなアップデートを提供する例データモデルについては、「 アトミック操作のモデルデータ 」を参照してください。
関連するデータ間の参照を保存するデータモデルの場合、アプリケーションはこれらの関連データを検索および変更するために、個別の読み取り操作と書込み操作を発行する必要があります。
MongoDB は(単一または複数のコレクション内の)複数のドキュメントへの読み取りと書込みにアトミック性を必要とする状況で、レプリカセットやシャーディングされたクラスターでのトランザクションを含む分散トランザクションをサポートします。
詳細については、「トランザクション」を参照してください。
重要
ほとんどの場合、分散トランザクションでは 1 つのドキュメントの書き込み (write) よりもパフォーマンス コストが高くなります。分散トランザクションの可用性は、効果的なスキーマ設計の代わりにはなりません。多くのシナリオにおいて、非正規化されたデータモデル(埋め込みドキュメントと配列)が引き続きデータやユースケースに最適です。つまり、多くのシナリオにおいて、データを適切にモデリングすることで、分散トランザクションの必要性を最小限に抑えることができます。
トランザクションの使用に関するその他の考慮事項(ランタイム制限や oplog サイズ制限など)については、「本番環境での考慮事項」も参照してください。
データライフサイクル管理
データ ライフサイクル マネジメントとは、データの作成とストレージからアーカイブと削除までのデータを管理するプロセスを指します。スキーマのコスト効率、パフォーマンス、セキュリティを確保するには、データモデリングの決定を行う際に データライフサイクル管理 を検討してください。
アプリケーションでデータベースに一定期間保持するデータが必要な場合は、 Time to Live または TTL 機能の使用を検討してください。例、TTL コレクションは、 ウェブアプリケーションでユーザーのログイン30 セッションを管理するのに役立ちます。セッションは 分間非アクティブになると自動的に期限切れになるように設定されています。つまり、 MongoDB は指定された期間後にセッション ドキュメントを自動的に削除するため、セッションコレクションを小さくして効率的なクエリを実行できます。
さらに、アプリケーションが最近挿入されたドキュメントのみを使用する場合は、 Capped コレクションの使用を検討してください。 Capped コレクションは、挿入されたドキュメントの先入れ先出し(FIFO)管理を提供し、挿入順序に基づいてドキュメントを挿入および読み取りする操作を効率的にサポートします。
ハードウェアの制約
スキーマを設計するときは、配置のハードウェア、特に使用可能な RAM の量を考慮してください。ドキュメントのサイズが大きくなると、RAM の使用量が増えるため、アプリケーションがディスクから読み取ることになり、パフォーマンスが低下する可能性があります。可能であれば、関連するフィールドのみがクエリによって返されるようにスキーマを設計します。この方法により、アプリケーションのワーキングセットが不必要に大きくならないようにすることができます。
小さいドキュメント
各 MongoDB ドキュメントには一定量のオーバーヘッドが含まれています。 このオーバーヘッドは通常は重要ではありませんが、コレクション内のドキュメントに 1 つまたは 2 つのフィールドしかない場合など、すべてのドキュメントが数バイトの場合は重要になります。
これらのコレクションのストレージ使用を最適化するための次の提案と戦略を検討してください。
_idフィールドを明示的に使用する。MongoDB クライアントは自動的に各ドキュメントに
_idフィールドを追加し、_idフィールド用に一意の12バイトのObjectIdを生成します。 さらに、MongoDB は常に_idフィールドをインデックス化します。 小さいドキュメントの場合、これはかなりの量のスペースを占める可能性があります。ストレージの使用を最適化するには、 ドキュメントをコレクションに挿入する ときに、
_idフィールドの値を明示的に指定できます。この戦略により、アプリケーションは_idフィールドにドキュメントの別の部分のスペースを持つ値を保存できます。_idフィールドには任意の値を保存できますが、この値はコレクション内のドキュメントのプライマリキーとして機能するため、ドキュメントを一意に識別する必要があります。 フィールドの値が一意でない場合、コレクション内で競合が発生するため、プライマリキーとして機能することはできません。より短いフィールド名を使用します。
注意
フィールド名を短縮するとMongoDBのBSONサイズを縮小できますが、ドキュメントモデル全体を変更してBSONサイズを縮小する方が効果的な場合が多いです。 フィールド名を短くすると表現力が低下する可能性があり、インデックスのサイズには影響しません。これは、フィールド名を組み込まない事前定義された構造があるためです。
MongoDB はすべてのフィールド名を各ドキュメントに保存します。 ほとんどのドキュメントにおいて、これはドキュメントで使用されるスペースのごく一部を表します。ただし、小さいドキュメントの場合、フィールド名はそれに比例して大きな量のスペースを表す場合があります。 次のような小さなドキュメントのコレクションを考えてみましょう。
{ last_name : "Smith", best_score: 3.9 } 次のように、
last_nameという名前のフィールドをlnameに、best_scoreという名前のフィールドをscoreに短縮すると、1 ドキュメントあたり9バイトを節約できます。{ lname : "Smith", score : 3.9 } ドキュメントを埋め込みます。
場合によっては、ドキュメントを他のドキュメントに埋め込み、ドキュメントごとのオーバーヘッドを節約したい場合があるかもしれません。 「 小さなドキュメントが多数含まれているコレクション 」を参照してください。
詳細
ドキュメントを構造化し、スキーマを定義する 方法の詳細については、 MongoDB University の データ モデリング コースを参照してください。