検索

ホームページ  >  に質問  >  本文

MongoDB 倾向于将数据都放在一个 Collection 下吗?

举个例子,有一个用户信息和用户间关系的数据库,如果按照 SQL 的思路,会建立用户信息和用户关系两张表。那么,在 MongoDB 中,是倾向于将用户关系嵌入到用户信号,组成一个单独的文档吗?

ringa_leeringa_lee2765日前747

全員に返信(3)返信します

  • 天蓬老师

    天蓬老师2017-04-24 09:11:33

    そうではありません。

    コレクション内の 1 つのドキュメントのサイズには上限があり、現在 16 MB であるため、すべてをコレクションに詰め込むことはできません。さらに、コレクション構造が複雑すぎると、クエリと更新の効率に影響を与えるだけでなく、メンテナンスの困難や運用上のリスクも引き起こします。手を振って誤ってドキュメントを null として保存しようとしたことはありますか? とにかく、すべての人の情報がこのコレクションに含まれている場合、それは非常に不快な気分になるはずです。

    一般原則は次のとおりです:

    • クエリメソッドに応じたクラスタリング

      • 頻繁に読み込む必要があるデータをまとめてください。
      • 論理的に密接に関連する情報をまとめます。
      • map-reduce/aggregation で必要なデータはまとめられており、これらの操作は 1 つのコレクションに対してのみ実行できます。
    • データ量に応じて分割

      • コレクション内で配列を使用する必要があり、配列の長さが増加し続けることがわかった場合は、データの内容を特別なコレクションに入れる必要があります。データの各部分は現在のコレクションの主キーを参照します。 doc (mysql の 1..N 外部キー依存関係と同様)。
      • 特定のドキュメントが深すぎる (2 層以上) とわかった場合は、おそらくそれを分割することを検討するでしょう。そうしないと、パフォーマンスと保守性に問題が発生します。
    • テーブル構造に応じたデザイン

      • MongoDB にはテーブル構造の概念がありませんが、実際の使用では、コレクション内にさまざまな構造のドキュメントが存在することはほとんどありません。ドキュメントの構造がますます異なってきていることに気付いた場合は、どうするかを検討する必要があります。それらを Structure のようなものに抽象化し、変更されたものを他のコレクションにスローし、外部キーの依存関係を使用して相互に参照します。

    たとえば、ユーザー システムを設計する場合、ユーザー コレクションには名前などの一般的に使用される情報と、ユーザーにのみ関連する lastLoginAt が含まれる必要があります。おそらく、ユーザーのアクセス権に関する情報も含める必要があります。ユーザーのログイン ログは含めるべきではありません。この情報は今後も増加し続けます。

    ユーザー間の関係については、ユーザーコレクションの有無について議論する必要があります。ユーザー間の関係を保存し、友人の UID を記録するだけでよく、友人の数がそれほど多くなく、多くても数百人であれば、私はそれらをコレクションに入れる傾向があります。関係データ自体が複雑である場合、または友人の数が数千人に達する場合は、データを分割する傾向があります。

    さらに、Mongodb の公式データ モデル設計パラダイムをよく読むことをお勧めします。

    返事
    0
  • 怪我咯

    怪我咯2017-04-24 09:11:33

    元のアドレス: http://pwhack.me/post/2014-06-25-1 転載する場合は出典を明記してください

    この記事は、「The Definitive Guide to MongoDB」の第 8 章からの抜粋であり、次の 2 つの質問に完全に答えることができます。

      /q/1010000000364944
    • /q/1010000000364944
    データを表現する方法はたくさんありますが、最も重要な問題の 1 つは、データをどの程度正規化する必要があるかです。正規化は、データを複数の異なるコレクションに分散するプロセスであり、異なるコレクションは相互にデータを参照できます。多くのドキュメントで特定のデータを参照できますが、このデータは 1 つのコレクションにのみ保存されます。したがって、このデータを変更する場合は、このデータを保存するドキュメントを変更するだけで済みます。ただし、MongoDB は結合ツールを提供していないため、異なるコレクション間で結合クエリを実行するには複数のクエリが必要です。

    非正規化は正規化の逆で、各ドキュメントに必要なデータをドキュメント内に埋め込みます。すべてのドキュメントがデータの同じコピーを集合的に参照するのではなく、各ドキュメントには独自のデータのコピーがあります。つまり、情報が変更された場合は、関連するすべてのドキュメントを更新する必要がありますが、クエリの実行時には、すべてのデータを取得するために 1 つのクエリだけが必要になります。

    いつ正規化し、いつ非正規化するかを決定するのは困難です。正規化によりデータの書き込み速度が向上し、非正規化によりデータの読み取り速度が向上します。これは、独自のアプリケーションの多数のニーズと照らし合わせて慎重に検討する必要があります。

    データ表現の例

    学生とコースの情報を保存したいとします。これを表す 1 つの方法は、学生コレクション (各学生がドキュメント) とクラス コレクション (各コースがドキュメント) を使用することです。次に、3 番目のコレクション studentsClasses を使用して、学生とコースの間の関係を保存します。

    リーリー

    リレーショナル データベースに精通している場合は、以前にこのタイプのテーブル結合を作成したことがあるかもしれません。ただし、各デメリット ドキュメントには (コース "_id" のリストではなく) 生徒が 1 人、コースが 1 つしかない場合があります。コースを配列に入れるのはちょっと MongoDB スタイルですが、実際には、実際の情報を取得するには多くのクエリが必要になるため、通常はこのようにデータを保存しません。

    学生が選択したコースを見つけたいとします。まず、students コレクションを検索して学生情報を見つけ、次に、studentClasses にクエリを実行して、コース "_id" を見つけ、最後に、classes コレクションにクエリを実行して、必要な情報を取得する必要があります。コース情報を確認するには、サーバーに 3 つのクエリを要求する必要があります。学生情報やコース情報が頻繁に変更され、データの読み取り速度が要求されない限り、MongoDB でこの種のデータ構成を使用することは望ましくないでしょう。

    学生ドキュメントにコース参照を埋め込む場合、クエリを保存できます:

    リーリー

    「classes」フィールドは、John Doe が受講する必要があるコースの「_id」を格納する配列です。これらのコースに関する情報を確認する必要がある場合、これらの「_id」を使用してクラス コレクションをクエリできます。このプロセスに必要なクエリは 2 つだけです。データを整理するこの方法は、データがいつでもアクセスする必要がなく、いつでも変更されない場合に最適です (「いつでも」のほうが「頻繁に」より要求が高くなります)。

    読み取り速度をさらに最適化する必要がある場合は、データを完全に非正規化し、コース情報を生徒ドキュメントの「クラス」フィールドに埋め込みドキュメントとして保存することができます。この方法では、のみで生徒のコース情報を取得できます。 1 つのクエリ:

    リーリー

    上記の方法の利点は、学生のコース情報を取得するために必要なクエリが 1 つだけであることです。欠点は、より多くのストレージ容量を必要とし、データの同期がより困難になることです。たとえば、物理学が (3 点の成績ではなく) 4 点の単位になった場合、物理学のコースを受講したすべての学生は、「物理学」のドキュメントだけでなく、ドキュメントを更新する必要があります。

    最後に、埋め込みデータと参照データを混合することもできます。サブドキュメント配列を作成して共通の情報を保存し、より詳細な情報をクエリする必要がある場合は、参照によって実際のドキュメントを検索します。 リーリー

    必要に応じて埋め込み情報を変更できるため、この方法も良い選択です。ページに追加する情報を増やす (または減らす) ことができます。情報は埋め込みに配置されます。書類。

    考慮すべきもう 1 つの重要な質問は、情報はより頻繁に更新されるのか、それともより頻繁に読まれるのかということです。データが定期的に更新される場合は、正規化を選択することをお勧めします。データが頻繁に変更されない場合、更新効率を最適化するために読み取りおよび書き込み速度を犠牲にする価値はありません。

    たとえば、教科書の正規化入門の例としては、ユーザーとユーザー アドレスを別のコレクションに保存することが考えられます。ただし、ユーザーがアドレスを変更することはめったにないため、誰かがアドレスを変更するという非常にまれな事態のために各クエリの効率が犠牲にされるべきではありません。この場合、アドレスをユーザー文書に埋め込む必要があります。

    インライン ドキュメントを使用する場合は、ドキュメントを更新するときに cron ジョブを設定して、更新のたびにすべてのドキュメントが正常に更新されるようにする必要があります。たとえば、更新を複数のドキュメントに分散しようとしたところ、すべてのドキュメントの更新が完了する前にサーバーがクラッシュしました。この問題を検出し、未完了のアップデートをやり直すことができる必要があります。

    一般的に言えば、データが生成される頻度が高くなるほど、他のドキュメントに埋め込まれる可能性は低くなります。埋め込みフィールドまたは埋め込みフィールドの数が無制限に増加する場合は、これらのコンテンツを別のコレクションに保存し、他のドキュメントに埋め込むのではなく、参照を使用してアクセスする必要があります。コメント リストやアクティビティ リストなどの情報は、別のコレクションに保存する必要があります。他の文書に埋め込まないでください。

    最後に、いくつかのフィールドがドキュメント データの一部である場合、これらのフィールドをドキュメントに埋め込む必要があります。ドキュメントのクエリ時にフィールドを除外する必要が頻繁にある場合は、このフィールドを現在のドキュメントに埋め込むのではなく、別のコレクションに配置する必要があります。

    埋め込みにより適しています 引用に適しています
    サブドキュメントが小さくなります サブドキュメントのサイズが大きくなります
    データは定期的に変更されません データは頻繁に変更されます
    最終データは一貫しています 中間段階のデータは一貫している必要があります
    ドキュメントデータが少し増えました ドキュメントデータが大幅に増加しました
    データを取得するには通常、二次クエリが必要です データは通常、結果に含まれません
    速読 高速書き込み

    ユーザー コレクションがあるとします。以下に、必須となる可能性のあるいくつかのフィールドと、それらのフィールドをユーザー文書に埋め込む必要があるかどうかを示します。

    ユーザー設定(アカウント設定)

    ユーザー設定は特定のユーザーにのみ関連するため、ユーザー文書内の他のユーザー情報を照会する必要がある可能性が高くなります。したがって、ユーザー設定をユーザー文書に埋め込む必要があります。

    最近の活動

    このフィールドは、最近のアクティビティの成長と変化の頻度によって異なります。これが固定長フィールド (最後の 10 件のイベントなど) の場合、このフィールドはユーザー文書に埋め込まれる必要があります。

    友達

    通常、少なくとも完全にではなく、友人情報をユーザードキュメントに埋め込むべきではありません。次のセクションでは、ソーシャル ネットワーク アプリケーションの関連コンテンツを紹介します。

    すべてのユーザー生成コンテンツ

    ユーザードキュメントに埋め込まないでください。

    ベース

    セットに含まれる他のコレクションへの参照の数は、カーディナリティと呼ばれます。一般的な関係には、1 対 1、1 対多、および多対多が含まれます。ブログ アプリケーションがあるとします。各ブログ投稿にはタイトルがあり、1 対 1 の関係になります。各著者は複数の記事を持つことができ、これは 1 対多の関係になります。各記事には複数のタグを含めることができ、各タグは複数の記事で使用できるため、これは多対多の関係になります。

    MongoDB では、多数は 2 つのサブカテゴリ (多数と少数) に分割できます。たとえば、著者と記事の関係は 1 対ほとんどの関係である可能性があります。つまり、各著者は少数の記事しか出版しません。ブログ投稿とタグの間には多対少数の関係がある場合があります。実際には、投稿の数がタグの数よりも多い場合があります。ブログ投稿とコメントの間には 1 対多の関係があり、各投稿には多数のコメントを含めることができます。

    少ないものと多いものの間の関係が決定されている限り、埋め込みデータと参照データの間でトレードオフを行うのは簡単です。一般に、「少ない」関係にはインライン方式を使用する方が適しており、「多い」関係には参照方式を使用する方が適しています。

    友達、ファン、その他面倒なこと

    友達を近くに保ち、敵から遠ざけてください

    多くのソーシャル アプリケーションでは、人、コンテンツ、ファン、友人などをリンクする必要があります。この関連性の高いデータに対して、インライン フォームと参照フォームの使用の間のトレードオフは簡単ではありません。このセクションでは、ソーシャル グラフ データに関する考慮事項を紹介します。多くの場合、フォロー、友達、またはお気に入りは、パブリッシュ/サブスクライブ システムに単純化できます。つまり、あるユーザーが別のユーザーに関連する通知をサブスクライブできます。このように、サブスクライバーを保存する方法と、すべてのサブスクライバーにイベントを通知する方法という 2 つの基本操作が効率的である必要があります。

    一般的なサブスクリプションの実装方法は 3 つあります。最初の方法は、購読者ドキュメントにコンテンツプロデューサーを埋め込むことです:

    リーリー

    これで、特定のユーザー ドキュメントについて、フォーム db.activities.find({"user": {"$in": user["following"]}}) を使用してユーザーが関心のあるすべてのアクティビティ情報をクエリできるようになります。ただし、公開されたばかりのアクティビティ情報の場合、その情報に興味があるすべてのユーザーを調べたい場合は、すべてのユーザーの「フォロー中」フィールドをクエリする必要があります。

    もう 1 つの方法は、プロデューサー ドキュメントにサブスクライバーを埋め込むことです:

    リーリー

    このプロデューサーが新しいメッセージを公開すると、どのユーザーに通知する必要があるかをすぐに知ることができます。この欠点は、ユーザーがフォローしているユーザーのリストを検索する必要がある場合、ユーザー コレクション全体をクエリする必要があることです。この方法の長所と短所は、最初の方法の長所と短所とはまったく逆になります。

    同時に、どちらの方法にも別の問題があります。それは、ユーザーのドキュメントがますます大きくなり、より頻繁に変更されることです。多くの場合、「フォロー中」フィールドと「フォロワー」フィールドは返す必要さえありません。フォロワーのリストはどのくらいの頻度でクエリされますか?ユーザーが特定の人をより頻繁にフォローしたり、一部の人をフォロー解除したりすると、多くの断片化が発生します。したがって、最終的なソリューションでは、データをさらに正規化し、サブスクリプション情報を別のコレクションに保存して、これらの欠点を回避します。この種の正規化は少し面倒かもしれませんが、頻繁に変更され、ドキュメントの残りの部分と一緒に返す必要がないフィールドには便利です。 「フォロワー」フィールドのこの正規化を行うことは理にかなっています。

    コレクションを使用してパブリッシャーとサブスクライバー間の関係を保存します。ドキュメント構造は次のようになります。

    リーリー

    これにより、ユーザードキュメントがより合理化されますが、ファンリストを取得するために追加のクエリが必要になります。 「フォロワー」配列のサイズは頻繁に変更されるため、このコレクションで「usePowerOf2Sizes」を有効にして、ユーザー コレクションができるだけ小さくなるようにすることができます。フォロワーのコレクションが別のデータベースに保存されている場合は、ユーザーのコレクションに大きな影響を与えることなく圧縮できます。

    ウィル・ウィートン効果への対処

    どのような戦略が使用されるかに関係なく、インラインフィールドは、サブドキュメントまたは参照の数が特に多くない場合にのみ効果的に機能します。より有名なユーザーの場合、ファン リストを保存するためのドキュメントがオーバーフローする可能性があります。この状況に対する解決策の 1 つは、必要に応じて「連続した」ドキュメントを使用することです。例:

    リーリー

    この状況では、アプリケーションの "tbc" (続き) 配列からデータを取得するための関連ロジックを追加する必要があります。

    何か言ってください

    特効薬はない

    返事
    0
  • 伊谢尔伦

    伊谢尔伦2017-04-24 09:11:33

    ビジネスがユーザー間の関係を常にクエリする必要がある場合は、関係をコレクションに分離することをお勧めします

    返事
    0
  • キャンセル返事