web-dev-qa-db-ja.com

mongodbのデータの重複が多すぎますか?

私はこのすべてのNOSQLに不慣れで、最近mongoDBに興味を持っています。私は新しいWebサイトをゼロから作成し、MONGODB/NORM(C#用)を唯一のデータベースとして使用することにしました。私はドキュメントモデルデータベースを適切に設計する方法について多くの記事を読んでいますが、ほとんどの場合、私の設計はかなりうまく機能していると思います。新しいサイトを開始して約6か月が経過し、データの複製や同期に関する問題が何度も何度も対処する必要が出てきました。私が読んだことから、これはドキュメントモデルで予期されていることであり、パフォーマンスにとっては理にかなっています。 I.E.埋め込みオブジェクトをドキュメントに貼り付けるので、読み込みが速く、結合はありません。もちろん、いつでも埋め込むことができるわけではないので、mongodbにはDbReferenceのこの概念があります。これは、基本的にリレーショナルDBの外部キーに類似しています。

ここに例を示します。ユーザーとイベントがあります。どちらも独自のドキュメントを取得します。ユーザーはイベントに参加します。イベントにはユーザーが参加します。データが制限されたイベントのリストをUserオブジェクトに埋め込むことにしました。また、ユーザーのリストを「出席者」としてイベントオブジェクトに埋め込みました。ここでの問題は、ユーザーを、イベントオブジェクトにも埋め込まれているユーザーのリストと同期させる必要があることです。私が読んだように、これは推奨されるアプローチであり、NOSQLによる方法です。取得は高速ですが、フォールバックはメインのユーザードキュメントを更新するときです。また、イベントオブジェクトに移動し、おそらくそのユーザーへのすべての参照を見つけて更新する必要もあります。

だから私が持っている質問は、これは人々が対処する必要があるかなり一般的な問題ですか? 「たぶん、NOSQL戦略が私がここでやろうとしていることと合わないかもしれません」と言う前に、この問題はどれくらい起こらなければなりませんか?埋め込みオブジェクトでデータの同期を維持するのに苦労し、DBへの複数の読み取りを実行するのに苦労しているため、結合を実行する必要がないというパフォーマンス上の利点はいつデメリットに変わりますか?

70
mike

まあそれはドキュメントストアとのトレードオフです。標準のRDMSと同じように正規化された方法で保存できます。できる限り正規化に努める必要があります。正規化を破ってデータ構造を平坦化する必要があるのは、パフォーマンスへの影響がある場所だけです。トレードオフは、読み取り効率と更新コストです。

Mongoには非常に効率的なインデックスがあり、従来のRDMSのように正規化を簡単に行うことができます(ほとんどのドキュメントストアでは、これを無料で提供していないため、Mongoは純粋なドキュメントストアではなくハイブリッドです。)これを使用して、ユーザーとイベント間の関係コレクションを作成できます。これは、表形式のデータストアの代理テーブルに似ています。イベントフィールドとユーザーフィールドにインデックスを付けると、かなり高速になり、データをより適切に正規化するのに役立ちます。

レコードデータの更新とクエリで必要なものの読み取りにかかる時間について、構造をフラット化する効率と正規化を維持する効率をプロットしたいと思います。大きなO表記法でそれを行うことができますが、それほど豪華である必要はありません。データの異なるモデルを使用したいくつかのユースケースに基づいて紙にいくつかの数字を書き、どれだけの作業が必要かについて良い直感を得ます。

基本的に、私が最初に行うことは、レコードが更新される回数と読み取られる頻度の確率を予測することです。次に、両方とも正規化またはフラット化されたとき(またはおそらく2つを部分的に組み合わせた場合...さまざまな最適化オプション)、更新のコストと読み取りのコストを予測します。次に、データをフラットに保つことによる節約と、正規化されたソースからデータを構築することのコストを比較できます。すべての変数をプロットしたら、それをフラットに保つことの節約が私にたくさん節約するなら、それから私はそれをフラットに保ちます。

いくつかのヒント:

  • 高速なルックアップを迅速かつアトミック(完全に最新)にする必要がある場合は、正規化よりもフラット化を優先し、更新にヒットするソリューションを選択することをお勧めします。
  • 更新を迅速に行い、すぐにアクセスする必要がある場合は、正規化を優先してください。
  • 高速なルックアップが必要だが、完全に最新のデータは必要ない場合は、正規化されたデータをバッチジョブで構築することを検討してください(おそらくmap/reduceを使用)。
  • クエリを高速にする必要があり、更新がまれであり、必ずしも更新にすぐにアクセスできる必要がない場合、または100%の時間を経過したトランザクションレベルのロックが必要な場合(更新がディスクに書き込まれたことを保証するため)、更新をバックグラウンドで処理するキューに書き込むことを検討できます。 (このモデルでは、おそらく競合の解決と調整を後で処理する必要があります)。
  • さまざまなモデルのプロファイルを作成します。コードでデータクエリ抽象化レイヤー(ある意味ORMのような)を構築して、後でデータストア構造をリファクタリングできるようにします。

あなたが採用できる他の多くのアイデアがあります。 highscalabilty.orgのようにそこに入るたくさんの素晴らしいブログがオンラインにあり、CAPの定理を理解していることを確認してください。

Redisやmemcacheなどのキャッシュレイヤーも検討してください。これらの製品の1つをデータレイヤーの前に配置します。 mongo(すべてを正規化して格納する)をクエリするときは、データを使用して、フラット化された表現を作成し、キャッシュに格納します。データを更新すると、更新しているものを参照するキャッシュ内のデータがすべて無効になります。 (ただし、スケーリング係数を考慮して更新されているキャッシュ内のデータと追跡データを無効にするのにかかる時間をとる必要があります)。誰かがかつて「コンピュータサイエンスで最も難しい2つのことは、名前を付けることとキャッシュを無効にすることです。」

お役に立てば幸いです。

58
Zac Bowling

タイプUserEventプロパティのIListをユーザーオブジェクトに追加してみてください。ドメインモデルの設計方法についてはあまり指定していません。例については、NoRMグループ http://groups.google.com/group/norm-mongodb/topics を確認してください。

0
Peter Bromberg