MongoDBスキーマ設計-小さなドキュメントが多いですか、それとも大きいドキュメントが少ないですか?


89

背景
RDBMSデータベースからMongoDBへの変換のプロトタイプを作成しています。非正規化している間、2つの選択肢があるように見えます。1つは数百万の小さなドキュメントにつながるか、もう1つは少数(数十万)の大きなドキュメントにつながるかです。

それを単純なアナログにまとめることができれば、次のような顧客ドキュメントが少ないコレクションとの違いになります(Javaの場合)。

クラス顧客{
    プライベート文字列名。
    プライベートアドレスアドレス;
    //各CreditCardには数百のPaymentインスタンスがあります
    プライベートセット<CreditCard>クレジットカード;
}

または、次のような多数の支払いドキュメントを含むコレクション:

クラス支払い{
    個人顧客顧客;
    プライベートクレジットカードクレジットカード;
    プライベート日付payDate;
    プライベートフロートpayAmount;
}

質問
MongoDBは、多数の小さなドキュメントを優先するように設計されていますか、それとも少数の大きなドキュメントを優先するように設計されていますか?答えは、実行する予定のクエリに大きく依存しますか?(つまり、顧客Xは何枚のクレジットカードを持っていますか?vsすべての顧客が先月支払った平均金額はいくらでしたか?)

私はよく調べましたが、質問に答えるのに役立つMongoDBスキーマのベストプラクティスに出くわすことはありませんでした。

回答:


82

あなたは間違いなくあなたがしているクエリのために最適化する必要があるでしょう。

これがあなたの説明に基づいた私の最善の推測です。

各顧客のすべてのクレジットカードを知りたいと思うかもしれないので、それらの配列を顧客オブジェクト内に保持します。また、支払いごとに顧客参照が必要になる場合もあります。これにより、支払い文書が比較的小さくなります。

Paymentオブジェクトには、自動的に独自のIDとインデックスがあります。おそらく、顧客参照にもインデックスを追加することをお勧めします。

これにより、顧客オブジェクト全体を毎回保存することなく、顧客による支払いをすばやく検索できます。

「すべての顧客が先月支払った平均金額」などの質問に答えたい場合は、代わりに、サイズの大きいデータセットのマップ/リデュースが必要になります。この応答は「リアルタイム」ではありません。Customerへの「参照」を保存することで、これらのmap-reduceにはおそらく十分であることがわかります。

それで、あなたの質問に直接答えるために:MongoDBは、多くの、多くの小さなドキュメントまたはより少ない大きなドキュメントを好むように設計されていますか?

MongoDBは、インデックス付きエントリを非常に迅速に見つけるように設計されています。MongoDBは、大きな干し草の山からいくつかの針を見つけるのに非常に優れています。MongoDBは、干し草の山の中のほとんどの針を見つけるのがあまり得意ではありません。したがって、最も一般的なユースケースを中心にデータを構築し、まれなユースケースのmap / reduceジョブを作成します。


31

MongoDB自身のドキュメントによると、多くの小さなドキュメント用に設計されているようです。

MongoDBのパフォーマンスのベストプラクティスから:

MongoDBのドキュメントの最大サイズは16MBです。実際には、ほとんどのドキュメントは数キロバイト以下です。テーブル自体よりもテーブルの行に似たドキュメントを検討してください。レコードのリストを単一のドキュメントに保持するのではなく、代わりに各レコードをドキュメントにします。

MongoDBスキーマ設計の6つの経験則から:パート1

1対数のモデリング

「1対数」の例としては、人の住所があります。これは、埋め込みの良いユースケースです。Personオブジェクト内の配列にアドレスを配置します。

1対多

「1対多」の例としては、交換部品注文システムの製品の部品があります。各製品には、最大で数百の交換部品が含まれる場合がありますが、数千程度を超えることはありません。これは参照の良い使用例です。製品ドキュメントの配列にパーツのObjectIDを配置します。

1対Squillions

「1対数」の例としては、さまざまなマシンのログメッセージを収集するイベントログシステムがあります。配列に格納したのがObjectIDだけであったとしても、任意のホストが16MBのドキュメントサイズをオーバーフローさせるのに十分なメッセージを生成する可能性があります。これは「親参照」の典型的な使用例です。ホストのドキュメントがあり、ログメッセージのドキュメントにホストのObjectIDを保存します。


12

時間の経過とともに大幅に増加するドキュメントは、時限爆弾を刻む可能性があります。ネットワーク帯域幅とRAM使用量が測定可能なボトルネックになる可能性があり、最初からやり直す必要があります。

まず、CustomerとPaymentの2つのコレクションについて考えてみましょう。したがって、粒子はかなり小さく、支払いごとに1つのドキュメントです。

次に、クレジットカードなどのアカウント情報をモデル化する方法を決定する必要があります。顧客のドキュメントにアカウント情報の配列が含まれているかどうか、または新しいアカウントコレクションが必要かどうかを考えてみましょう。

アカウントドキュメントが顧客ドキュメントとは別の場合、1人の顧客のすべてのアカウントをメモリにロードするには、複数のドキュメントをフェッチする必要があります。これは、余分なメモリ、I / O、帯域幅、およびCPU使用率につながる可能性があります。それはすぐにアカウントの収集が悪い考えであることを意味しますか?

あなたの決定は支払書類に影響します。アカウント情報が顧客ドキュメントに埋め込まれている場合、どのように参照しますか?個別のアカウントドキュメントには、独自の_id属性があります。アカウント情報が埋め込まれている場合、アプリケーションはアカウントの新しいIDを生成するか、アカウントの属性(アカウント番号など)をキーに使用します。

支払い文書には、実際には一定の時間枠(たとえば、日)に行われたすべての支払いを含めることができますか?このような複雑さは、支払い文書を読み書きするすべてのコードに影響します。時期尚早の最適化は、プロジェクトにとって致命的となる可能性があります。

アカウントドキュメントと同様に、支払いドキュメントに支払いが1つしかない限り、支払いは簡単に参照できます。新しいタイプのドキュメント、たとえばクレジットは、支払いを参照できます。しかし、クレジットコレクションを作成しますか、それとも支払い情報内にクレジット情報を埋め込みますか?後でクレジットを参照する必要がある場合はどうなりますか?

要約すると、私はたくさんの小さなドキュメントとたくさんのコレクションで成功しています。_idを使用し、_idのみを使用して参照を実装します。したがって、増え続けるドキュメントがアプリケーションを破壊する心配はありません。各エンティティには独自のコレクションがあるため、スキーマは理解しやすく、インデックスを付けるのも簡単です。重要なエンティティは他のドキュメントの中に隠れていません。

私はあなたの発見について聞いてみたいです。幸運を!

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.