ユーザーフォローシステムを実装したい。ユーザーは他のユーザーをフォローできます。私は2つのアプローチを検討しています。1つは、スキーマに存在followers
しfollowees
、User
両方ともユーザーの配列であるということ_id
です。もう1つはfollowers
、スキーマにのみ存在することです。ユーザーのフォロワーを見つけたいときはいつでも、すべてのユーザーのfollowers
配列、つまりを検索する必要がありdb.user.find( { followers: "_id" } );
ます。2つのアプローチの長所と短所は何ですか?ありがとう。
2 に答える
あなたが検討しているのは、ここでの古典的な「多対多」の関係です。このスキーマに単一の「正しい」正規形があるRDBMSとは異なり、MongoDBでは、正しいスキーマ設計は、データの使用方法や、ここで言及していない他のいくつかの要因によって異なります。 。
この説明では、「フォロー」関係は対称的ではないと想定していることに注意してください。つまり、BがAをフォローしなくても、AはBをフォローできます。
1)MongoDBでこの関係をモデル化する基本的な方法は2つあります。
- インデックス付きの「次の」配列をユーザードキュメントに埋め込むことができます。
次のように、「次の」ドキュメントの個別のコレクションを作成できます。
{ユーザー:ObjectID( "x")、次:ObjectID( "y")}
このコレクションには、次の関係ごとに1つのドキュメントがあります。このコレクションには、「ユーザー」用と「フォロー中」用の2つのインデックスが必要です。
質問の2番目の提案(ユーザードキュメントに「following」と「followed」の両方の配列がある)は、最初の提案の単なるバリエーションであることに注意してください。
2)正しい設計は、ここで言及していないいくつかの要因によって異なります。
- 1人で何人のフォロワーを獲得できますか?また、1人で何人のフォロワーをフォローできますか?
- 最も一般的なクエリは何ですか?フォロワーのリストを表示するのですか、それともフォローされているユーザーのリストを表示するのですか?
- フォロワー/フォローリストを更新する頻度はどれくらいですか?
3)トレードオフは次のとおりです。
埋め込み配列アプローチの利点は、コードが単純であり、フォローしているユーザーの配列全体を1つのドキュメントでフェッチできることです。'following'配列にインデックスを付けると、そのインデックスが完全にRAMに収まる限り、すべてのユーザーフォロワーを見つけるクエリは比較的高速になります。(これはリレーショナルデータベースと同じです。)
フォロワーを頻繁に更新する場合、またはフォロワー/フォローの数に制限がない場合、組み込み配列アプローチの欠点が発生します。
無制限の数のフォロワー/フォローを許可すると、MongoDBドキュメントの最大サイズをオーバーフローさせる可能性があります。10万人以上のフォロワーがいることは前代未聞ではありません。この場合は、別の収集アプローチに進む必要があります。
フォロワーが頻繁に更新されることがわかっている場合は、別の収集アプローチも使用することをお勧めします。その理由は、フォロワーを追加するたびに、「フォロワー」配列のサイズが大きくなるためです。特定のサイズに達すると、ディスク上で予約されているスペースの量を超えてしまい、MongoDBはドキュメントを移動する必要があります。そのドキュメントのすべてのインデックスも更新する必要があるため、これにより追加の書き込みオーバーヘッドが発生します。
4)組み込み配列アプローチを使用する場合は、それをより実現可能にするために実行できることがいくつかあります。
まず、1人が持つことができるフォロワーの総数を制限することができます。次に、新しいユーザーを作成するときに、多数のダミーフォロワーが事前に作成されたドキュメントを作成できます。(たとえば、「followers」配列に、実際のユーザーを参照していないことがわかっている多数のエントリ(ID 0など)を入力します。)このようにして、新しいフォロワーを追加するときに、IDの1つを置き換えます。 0エントリで実際のエントリがあり、ドキュメントサイズは大きくなりません。
次に、誰かが持つことができるフォロワーの数を制限し、アプリケーションでそれを確認することができます。
ドキュメントで2配列のアプローチを使用する場合、1人が持つことができるフォロワーの最大数を削減することに注意してください(ドキュメントの一部は、フォローしているユーザーの配列で占められるため)。
5)最適化として、バケット化される「次の」ドキュメントを変更できます。したがって、後続の関係ごとに1つのドキュメントの代わりに、ユーザーごとにバケット化することができます。
{ user: "X", following: [ "A", "B", "C" ... ] }
{ user: "X", following: [ "H", "I", "J" ... ] }
{ user: "Y", following: [ "A", "X", "K" ... ] }
6)多対多をモデル化する方法の詳細については、次のプレゼンテーションを参照してください。
「バケット化」デザインパターンの詳細については、MongoDBドキュメントの次のエントリを参照してください。
両方を提供するfollowers
とfollowees
、これらのフィールドのいずれかにセカンダリインデックスがなくても、ほとんどのクエリを効率的に処理できる可能性があります。たとえば、現在のユーザーを取得してから、_idのデフォルトのインデックスを使用して、すべての接続のリストを取得できます。
db.users.find({_id: {$in: user_A.followers}})
を含めない場合、コレクションスキャンなしで一部のクエリを処理するにはfollowees
、にセカンダリインデックスを作成する必要があります。followers
たとえば、ユーザーAのすべてのフォロワーを特定するには、次のようにクエリを使用します。
db.users.find({followers: user_A._id})
セカンダリインデックスは、メモリとディスクスペースをいくらか消費しますが、潜在的なデータの不整合(フォロワーリストとフォロワーリストの不一致)を回避します。