举个例子,有一个用户信息和用户间关系的数据库,如果按照 SQL 的思路,会建立用户信息和用户关系两张表。那么,在 MongoDB 中,是倾向于将用户关系嵌入到用户信号,组成一个单独的文档吗?
ringa_lee
そうではありません。
コレクション内の 1 つのドキュメントのサイズには上限があり、現在 16 MB であるため、すべてをコレクションに詰め込むことはできません。さらに、コレクション構造が複雑すぎると、クエリと更新の効率に影響を与えるだけでなく、メンテナンスの困難や運用上のリスクも引き起こします。手を振って誤ってドキュメントを null として保存しようとしたことはありますか? とにかく、すべての人の情報がこのコレクションに含まれている場合、それは非常に不快な気分になるはずです。
一般原則は次のとおりです:
たとえば、ユーザー システムを設計する場合、ユーザー コレクションには名前などの一般的に使用される情報と、ユーザーにのみ関連する lastLoginAt が含まれる必要があります。おそらく、ユーザーのアクセス権に関する情報も含める必要があります。ユーザーのログイン ログは含めるべきではありません。この情報は今後も増加し続けます。
ユーザー間の関係については、ユーザーコレクションの有無について議論する必要があります。ユーザー間の関係を保存し、友人の UID を記録するだけでよく、友人の数がそれほど多くなく、多くても数百人であれば、私はそれらをコレクションに入れる傾向があります。関係データ自体が複雑である場合、または友人の数が数千人に達する場合は、データを分割する傾向があります。
さらに、Mongodb の公式データ モデル設計パラダイムをよく読むことをお勧めします。
元のアドレス: http://pwhack.me/post/2014-06-25-1 転載する場合は出典を明記してください
この記事は、「The Definitive Guide to MongoDB」の第 8 章からの抜粋であり、次の 2 つの質問に完全に答えることができます。
リーリー
「classes」フィールドは、John Doe が受講する必要があるコースの「_id」を格納する配列です。これらのコースに関する情報を確認する必要がある場合、これらの「_id」を使用してクラス コレクションをクエリできます。このプロセスに必要なクエリは 2 つだけです。データを整理するこの方法は、データがいつでもアクセスする必要がなく、いつでも変更されない場合に最適です (「いつでも」のほうが「頻繁に」より要求が高くなります)。
読み取り速度をさらに最適化する必要がある場合は、データを完全に非正規化し、コース情報を生徒ドキュメントの「クラス」フィールドに埋め込みドキュメントとして保存することができます。この方法では、のみで生徒のコース情報を取得できます。 1 つのクエリ:
上記の方法の利点は、学生のコース情報を取得するために必要なクエリが 1 つだけであることです。欠点は、より多くのストレージ容量を必要とし、データの同期がより困難になることです。たとえば、物理学が (3 点の成績ではなく) 4 点の単位になった場合、物理学のコースを受講したすべての学生は、「物理学」のドキュメントだけでなく、ドキュメントを更新する必要があります。
最後に、埋め込みデータと参照データを混合することもできます。サブドキュメント配列を作成して共通の情報を保存し、より詳細な情報をクエリする必要がある場合は、参照によって実際のドキュメントを検索します。 リーリー
最後に、いくつかのフィールドがドキュメント データの一部である場合、これらのフィールドをドキュメントに埋め込む必要があります。ドキュメントのクエリ時にフィールドを除外する必要が頻繁にある場合は、このフィールドを現在のドキュメントに埋め込むのではなく、別のコレクションに配置する必要があります。
ユーザー コレクションがあるとします。以下に、必須となる可能性のあるいくつかのフィールドと、それらのフィールドをユーザー文書に埋め込む必要があるかどうかを示します。
ユーザー設定は特定のユーザーにのみ関連するため、ユーザー文書内の他のユーザー情報を照会する必要がある可能性が高くなります。したがって、ユーザー設定をユーザー文書に埋め込む必要があります。
このフィールドは、最近のアクティビティの成長と変化の頻度によって異なります。これが固定長フィールド (最後の 10 件のイベントなど) の場合、このフィールドはユーザー文書に埋め込まれる必要があります。
通常、少なくとも完全にではなく、友人情報をユーザードキュメントに埋め込むべきではありません。次のセクションでは、ソーシャル ネットワーク アプリケーションの関連コンテンツを紹介します。
ユーザードキュメントに埋め込まないでください。
セットに含まれる他のコレクションへの参照の数は、カーディナリティと呼ばれます。一般的な関係には、1 対 1、1 対多、および多対多が含まれます。ブログ アプリケーションがあるとします。各ブログ投稿にはタイトルがあり、1 対 1 の関係になります。各著者は複数の記事を持つことができ、これは 1 対多の関係になります。各記事には複数のタグを含めることができ、各タグは複数の記事で使用できるため、これは多対多の関係になります。
MongoDB では、多数は 2 つのサブカテゴリ (多数と少数) に分割できます。たとえば、著者と記事の関係は 1 対ほとんどの関係である可能性があります。つまり、各著者は少数の記事しか出版しません。ブログ投稿とタグの間には多対少数の関係がある場合があります。実際には、投稿の数がタグの数よりも多い場合があります。ブログ投稿とコメントの間には 1 対多の関係があり、各投稿には多数のコメントを含めることができます。
少ないものと多いものの間の関係が決定されている限り、埋め込みデータと参照データの間でトレードオフを行うのは簡単です。一般に、「少ない」関係にはインライン方式を使用する方が適しており、「多い」関係には参照方式を使用する方が適しています。
友達を近くに保ち、敵から遠ざけてください
多くのソーシャル アプリケーションでは、人、コンテンツ、ファン、友人などをリンクする必要があります。この関連性の高いデータに対して、インライン フォームと参照フォームの使用の間のトレードオフは簡単ではありません。このセクションでは、ソーシャル グラフ データに関する考慮事項を紹介します。多くの場合、フォロー、友達、またはお気に入りは、パブリッシュ/サブスクライブ システムに単純化できます。つまり、あるユーザーが別のユーザーに関連する通知をサブスクライブできます。このように、サブスクライバーを保存する方法と、すべてのサブスクライバーにイベントを通知する方法という 2 つの基本操作が効率的である必要があります。
一般的なサブスクリプションの実装方法は 3 つあります。最初の方法は、購読者ドキュメントにコンテンツプロデューサーを埋め込むことです:
これで、特定のユーザー ドキュメントについて、フォーム db.activities.find({"user": {"$in": user["following"]}}) を使用してユーザーが関心のあるすべてのアクティビティ情報をクエリできるようになります。ただし、公開されたばかりのアクティビティ情報の場合、その情報に興味があるすべてのユーザーを調べたい場合は、すべてのユーザーの「フォロー中」フィールドをクエリする必要があります。
db.activities.find({"user": {"$in": user["following"]}})
もう 1 つの方法は、プロデューサー ドキュメントにサブスクライバーを埋め込むことです:
このプロデューサーが新しいメッセージを公開すると、どのユーザーに通知する必要があるかをすぐに知ることができます。この欠点は、ユーザーがフォローしているユーザーのリストを検索する必要がある場合、ユーザー コレクション全体をクエリする必要があることです。この方法の長所と短所は、最初の方法の長所と短所とはまったく逆になります。
同時に、どちらの方法にも別の問題があります。それは、ユーザーのドキュメントがますます大きくなり、より頻繁に変更されることです。多くの場合、「フォロー中」フィールドと「フォロワー」フィールドは返す必要さえありません。フォロワーのリストはどのくらいの頻度でクエリされますか?ユーザーが特定の人をより頻繁にフォローしたり、一部の人をフォロー解除したりすると、多くの断片化が発生します。したがって、最終的なソリューションでは、データをさらに正規化し、サブスクリプション情報を別のコレクションに保存して、これらの欠点を回避します。この種の正規化は少し面倒かもしれませんが、頻繁に変更され、ドキュメントの残りの部分と一緒に返す必要がないフィールドには便利です。 「フォロワー」フィールドのこの正規化を行うことは理にかなっています。
コレクションを使用してパブリッシャーとサブスクライバー間の関係を保存します。ドキュメント構造は次のようになります。
これにより、ユーザードキュメントがより合理化されますが、ファンリストを取得するために追加のクエリが必要になります。 「フォロワー」配列のサイズは頻繁に変更されるため、このコレクションで「usePowerOf2Sizes」を有効にして、ユーザー コレクションができるだけ小さくなるようにすることができます。フォロワーのコレクションが別のデータベースに保存されている場合は、ユーザーのコレクションに大きな影響を与えることなく圧縮できます。
どのような戦略が使用されるかに関係なく、インラインフィールドは、サブドキュメントまたは参照の数が特に多くない場合にのみ効果的に機能します。より有名なユーザーの場合、ファン リストを保存するためのドキュメントがオーバーフローする可能性があります。この状況に対する解決策の 1 つは、必要に応じて「連続した」ドキュメントを使用することです。例:
この状況では、アプリケーションの "tbc" (続き) 配列からデータを取得するための関連ロジックを追加する必要があります。
特効薬はない
ビジネスがユーザー間の関係を常にクエリする必要がある場合は、関係をコレクションに分離することをお勧めします
そうではありません。
コレクション内の 1 つのドキュメントのサイズには上限があり、現在 16 MB であるため、すべてをコレクションに詰め込むことはできません。さらに、コレクション構造が複雑すぎると、クエリと更新の効率に影響を与えるだけでなく、メンテナンスの困難や運用上のリスクも引き起こします。手を振って誤ってドキュメントを null として保存しようとしたことはありますか? とにかく、すべての人の情報がこのコレクションに含まれている場合、それは非常に不快な気分になるはずです。
一般原則は次のとおりです:
たとえば、ユーザー システムを設計する場合、ユーザー コレクションには名前などの一般的に使用される情報と、ユーザーにのみ関連する lastLoginAt が含まれる必要があります。おそらく、ユーザーのアクセス権に関する情報も含める必要があります。ユーザーのログイン ログは含めるべきではありません。この情報は今後も増加し続けます。
ユーザー間の関係については、ユーザーコレクションの有無について議論する必要があります。ユーザー間の関係を保存し、友人の UID を記録するだけでよく、友人の数がそれほど多くなく、多くても数百人であれば、私はそれらをコレクションに入れる傾向があります。関係データ自体が複雑である場合、または友人の数が数千人に達する場合は、データを分割する傾向があります。
さらに、Mongodb の公式データ モデル設計パラダイムをよく読むことをお勧めします。
元のアドレス: http://pwhack.me/post/2014-06-25-1 転載する場合は出典を明記してください
この記事は、「The Definitive Guide to MongoDB」の第 8 章からの抜粋であり、次の 2 つの質問に完全に答えることができます。
/q/1010000000364944-
/q/1010000000364944
-
データを表現する方法はたくさんありますが、最も重要な問題の 1 つは、データをどの程度正規化する必要があるかです。正規化は、データを複数の異なるコレクションに分散するプロセスであり、異なるコレクションは相互にデータを参照できます。多くのドキュメントで特定のデータを参照できますが、このデータは 1 つのコレクションにのみ保存されます。したがって、このデータを変更する場合は、このデータを保存するドキュメントを変更するだけで済みます。ただし、MongoDB は結合ツールを提供していないため、異なるコレクション間で結合クエリを実行するには複数のクエリが必要です。リーリー
リレーショナル データベースに精通している場合は、以前にこのタイプのテーブル結合を作成したことがあるかもしれません。ただし、各デメリット ドキュメントには (コース "_id" のリストではなく) 生徒が 1 人、コースが 1 つしかない場合があります。コースを配列に入れるのはちょっと MongoDB スタイルですが、実際には、実際の情報を取得するには多くのクエリが必要になるため、通常はこのようにデータを保存しません。リーリー
「classes」フィールドは、John Doe が受講する必要があるコースの「_id」を格納する配列です。これらのコースに関する情報を確認する必要がある場合、これらの「_id」を使用してクラス コレクションをクエリできます。このプロセスに必要なクエリは 2 つだけです。データを整理するこの方法は、データがいつでもアクセスする必要がなく、いつでも変更されない場合に最適です (「いつでも」のほうが「頻繁に」より要求が高くなります)。
読み取り速度をさらに最適化する必要がある場合は、データを完全に非正規化し、コース情報を生徒ドキュメントの「クラス」フィールドに埋め込みドキュメントとして保存することができます。この方法では、のみで生徒のコース情報を取得できます。 1 つのクエリ:
リーリー上記の方法の利点は、学生のコース情報を取得するために必要なクエリが 1 つだけであることです。欠点は、より多くのストレージ容量を必要とし、データの同期がより困難になることです。たとえば、物理学が (3 点の成績ではなく) 4 点の単位になった場合、物理学のコースを受講したすべての学生は、「物理学」のドキュメントだけでなく、ドキュメントを更新する必要があります。
最後に、埋め込みデータと参照データを混合することもできます。サブドキュメント配列を作成して共通の情報を保存し、より詳細な情報をクエリする必要がある場合は、参照によって実際のドキュメントを検索します。 リーリー
必要に応じて埋め込み情報を変更できるため、この方法も良い選択です。ページに追加する情報を増やす (または減らす) ことができます。情報は埋め込みに配置されます。書類。最後に、いくつかのフィールドがドキュメント データの一部である場合、これらのフィールドをドキュメントに埋め込む必要があります。ドキュメントのクエリ時にフィールドを除外する必要が頻繁にある場合は、このフィールドを現在のドキュメントに埋め込むのではなく、別のコレクションに配置する必要があります。
ユーザー コレクションがあるとします。以下に、必須となる可能性のあるいくつかのフィールドと、それらのフィールドをユーザー文書に埋め込む必要があるかどうかを示します。
ユーザー設定(アカウント設定)
ユーザー設定は特定のユーザーにのみ関連するため、ユーザー文書内の他のユーザー情報を照会する必要がある可能性が高くなります。したがって、ユーザー設定をユーザー文書に埋め込む必要があります。
最近の活動
このフィールドは、最近のアクティビティの成長と変化の頻度によって異なります。これが固定長フィールド (最後の 10 件のイベントなど) の場合、このフィールドはユーザー文書に埋め込まれる必要があります。
友達
通常、少なくとも完全にではなく、友人情報をユーザードキュメントに埋め込むべきではありません。次のセクションでは、ソーシャル ネットワーク アプリケーションの関連コンテンツを紹介します。
すべてのユーザー生成コンテンツ
ユーザードキュメントに埋め込まないでください。
ベース
セットに含まれる他のコレクションへの参照の数は、カーディナリティと呼ばれます。一般的な関係には、1 対 1、1 対多、および多対多が含まれます。ブログ アプリケーションがあるとします。各ブログ投稿にはタイトルがあり、1 対 1 の関係になります。各著者は複数の記事を持つことができ、これは 1 対多の関係になります。各記事には複数のタグを含めることができ、各タグは複数の記事で使用できるため、これは多対多の関係になります。
MongoDB では、多数は 2 つのサブカテゴリ (多数と少数) に分割できます。たとえば、著者と記事の関係は 1 対ほとんどの関係である可能性があります。つまり、各著者は少数の記事しか出版しません。ブログ投稿とタグの間には多対少数の関係がある場合があります。実際には、投稿の数がタグの数よりも多い場合があります。ブログ投稿とコメントの間には 1 対多の関係があり、各投稿には多数のコメントを含めることができます。
少ないものと多いものの間の関係が決定されている限り、埋め込みデータと参照データの間でトレードオフを行うのは簡単です。一般に、「少ない」関係にはインライン方式を使用する方が適しており、「多い」関係には参照方式を使用する方が適しています。
友達、ファン、その他面倒なこと
多くのソーシャル アプリケーションでは、人、コンテンツ、ファン、友人などをリンクする必要があります。この関連性の高いデータに対して、インライン フォームと参照フォームの使用の間のトレードオフは簡単ではありません。このセクションでは、ソーシャル グラフ データに関する考慮事項を紹介します。多くの場合、フォロー、友達、またはお気に入りは、パブリッシュ/サブスクライブ システムに単純化できます。つまり、あるユーザーが別のユーザーに関連する通知をサブスクライブできます。このように、サブスクライバーを保存する方法と、すべてのサブスクライバーにイベントを通知する方法という 2 つの基本操作が効率的である必要があります。
一般的なサブスクリプションの実装方法は 3 つあります。最初の方法は、購読者ドキュメントにコンテンツプロデューサーを埋め込むことです:
リーリーこれで、特定のユーザー ドキュメントについて、フォーム
db.activities.find({"user": {"$in": user["following"]}})
を使用してユーザーが関心のあるすべてのアクティビティ情報をクエリできるようになります。ただし、公開されたばかりのアクティビティ情報の場合、その情報に興味があるすべてのユーザーを調べたい場合は、すべてのユーザーの「フォロー中」フィールドをクエリする必要があります。もう 1 つの方法は、プロデューサー ドキュメントにサブスクライバーを埋め込むことです:
リーリーこのプロデューサーが新しいメッセージを公開すると、どのユーザーに通知する必要があるかをすぐに知ることができます。この欠点は、ユーザーがフォローしているユーザーのリストを検索する必要がある場合、ユーザー コレクション全体をクエリする必要があることです。この方法の長所と短所は、最初の方法の長所と短所とはまったく逆になります。
同時に、どちらの方法にも別の問題があります。それは、ユーザーのドキュメントがますます大きくなり、より頻繁に変更されることです。多くの場合、「フォロー中」フィールドと「フォロワー」フィールドは返す必要さえありません。フォロワーのリストはどのくらいの頻度でクエリされますか?ユーザーが特定の人をより頻繁にフォローしたり、一部の人をフォロー解除したりすると、多くの断片化が発生します。したがって、最終的なソリューションでは、データをさらに正規化し、サブスクリプション情報を別のコレクションに保存して、これらの欠点を回避します。この種の正規化は少し面倒かもしれませんが、頻繁に変更され、ドキュメントの残りの部分と一緒に返す必要がないフィールドには便利です。 「フォロワー」フィールドのこの正規化を行うことは理にかなっています。
コレクションを使用してパブリッシャーとサブスクライバー間の関係を保存します。ドキュメント構造は次のようになります。
リーリーこれにより、ユーザードキュメントがより合理化されますが、ファンリストを取得するために追加のクエリが必要になります。 「フォロワー」配列のサイズは頻繁に変更されるため、このコレクションで「usePowerOf2Sizes」を有効にして、ユーザー コレクションができるだけ小さくなるようにすることができます。フォロワーのコレクションが別のデータベースに保存されている場合は、ユーザーのコレクションに大きな影響を与えることなく圧縮できます。
ウィル・ウィートン効果への対処
どのような戦略が使用されるかに関係なく、インラインフィールドは、サブドキュメントまたは参照の数が特に多くない場合にのみ効果的に機能します。より有名なユーザーの場合、ファン リストを保存するためのドキュメントがオーバーフローする可能性があります。この状況に対する解決策の 1 つは、必要に応じて「連続した」ドキュメントを使用することです。例:
リーリーこの状況では、アプリケーションの "tbc" (続き) 配列からデータを取得するための関連ロジックを追加する必要があります。
何か言ってください
特効薬はない
ビジネスがユーザー間の関係を常にクエリする必要がある場合は、関係をコレクションに分離することをお勧めします