スキーマデザイン

based on v20

導入

Mongoでは、リレーショナルデータベースのデザインでするような"正規化"はそれほど必要ありません。サーバサイドでの"join"がないからです。一般的には、1つの最上位のオブジェクトレベルに対して、1つのデータベースコレクションを作ることになるでしょう。

すべての"class"について、コレクションを作ることはあまりしません。代わりにembedオブジェクトを使います。例えば、下記の図では、studentsとcoursesの2つのコレクションがあります。studentドキュメントは、courseコレクションを参照する"address"ドキュメントをembedします。

scoreを他のテーブルに入れ、外部キーとしてstudentテーブルにリレーションを持つことになるであろうリレーショナルなスキーマと対比してみてください。

Embed (組み込み) vs. Reference (参照)

Mongoで、スキーマデザインをするときに考えるポイントは、"オブジェクトを自分のコレクションとして持つことにメリットがあるか? または、 他のコレクションのオブジェクトにembedするべきか?" です。

リレーショナルデータベースでは、サブアイテムは通常、(パフェーマンスのために正規化をしない、という場合を除き)別のテーブルにします。Mongoでは、これは推奨されません。オブジェクトをembedする方が効率がいいからです。

データはディスクの同じ場所に保存され、クライアントーサーバの通信回数も減ります。つまり、”なぜ、embedオブジェクトにしたくないのか?” ということを自問することになります。

なぜ、Referenceは遅いんでしょうか? 上記のstudentの例を考えてみましょう。 studentオブジェクトで、以下を実行します。

print( student.address.city );

この操作は、addressはembedオブジェクトなので常に速いです。また、studentがRAMにある場合、addressもRAMに入っています。しかし、

print( student.scores[0].for_course.name );

もしこれが、最初の scores[0] へのアクセスだった場合、シェルまたは、ドライバーは以下のQueryを実行することになります。

// pseudocode for driver or framework, not user code

student.scores[0].for_course = db.courses.findOne({_id:_course_id_to_find_});

つまり、それぞれのreferenceがデータベースのクエリになります。通常、 questionのコレクションでは、 _id にはインデックスが設定されています。そのためクエリーは速いです。しかし、たとえ、すべてのデータがRAMに入っていたとしても、アプリケーションサーバからデータベースのクライアント、サーバ間のコミュニケーションには遅延があります。一般的に、RAMのキャッシュにヒットする場合、1msぐらいの応答時間が期待されます。しかし、1,000 studentsをイテレートする場合では、1 studentで1回referenceを参照するだけで、たとえキャッシュされていたとしても、全体で1秒以上かかることになります。しかし、もし1つのアイテムだけのルックアップでよければ、この時間は1msのオーダーになり、webのページロードで受け入れられるような時間になります。(注意: dbキャッシュにすでに入っている場合、1,000 studentのロードは、データベースから結果がバッチで戻るので、もしかすると1秒よりかなり速いかもしれません。)

どのようなときにembedにするか、またはreferenceにするか、といくつかの一般的なルールです。

  • トップレベルにあるような"基本のクラス"は、専用のコレクションを持ちます。
  • アイテムの詳細のようなものはembedにします。
  • オブジェクトが、他のオブジェクトを"含む"ようなリレーションでは、embedにします。
  • 多対多のようなリレーションはreferenceにします。
  • 少しのオブジェクトだけを格納するようなコレクションは、別のコレクションにしても安全でしょう。なぜなら、コレクションすべてをアプリケーションサーバ側で簡単にキャッシュできるからです。
  • embedなオブジェクトは、"top level"なオブジェクトよりも参照するのが大変です。embedなオブジェクトへの参照を持つことができないからです。(少なくとも今のところは)
  • embedオブジェクトに対して、システムレベルでの検索結果を取得するのは難しいです。たとえば、scoreがもしembedでなければ、すべてのユーザの中からトップ100のscoreを取得するクエリーは簡単になります。
  • embedのデータが巨大(メガバイトクラス)になると、一つのオブジェクトのサイズの制限になるかもしれません
  • パフォーマンスが問題な場合、embed を使います。

ユースケース

いくつかのユースケースを検討してみましょう。

  1. Customer / Order / Order Line-Item
  • orders はコレクションにすべきでしょう。 customers もコレクション。 line-itemは order オブジェクトにembedされる配列にすべきでしょう。
  1. ブログシステム
  • posts はコレクションにします。 post author は別のコレクションがいいかもしれませんし、もしemailアドレスだけを保存のような場合には、単純に post のフィールドでもいいかもしれません。 comments はパフォーマンスのためにpostにembedがいいでしょう。

インデックスの設定

スキーマデザインをするときの次の検討事項はインデックスについてです。一般的なルールとしては、 リレーショナルデータベースでインデックスを設定したいようなところに、Mongoでもインデックスを作成します。

  • _id は自動的にインデックスが設定されます。
  • 検索されるキーにはインデックスを設定するべきでしょう。
  • ソート対象のフィールドも一般的にはインデックスが設定されるべきです。

MongoDBプロファイリング機能は、インデックスを張るべきところの情報を提供します。

インデックスの追加はコレクションに対する書き込みを遅くすることに注意してください。書き込みに対して読み込みが多いコレクションにはたくさんインデックスを使ってください(ストレージをたくさん使うことを気にしない場合)。読み込みより書き込みが多いコレクションでは、インデックスはとても負荷が高いです。

コレクションの数

Mongoのコレクションは多様な種類のデータを持てるので、一つのコレクションにすべての オブジェクト を入れることもできます。 このアプローチはいくつかのオブジェクトデータベースで取られています。しかしパフォーマンス的な理由から、このアプローチは推奨しません。Mongoでは、一つのコレクションの中のデータは、ディスクの中で連続して置かれる傾向があります。そのため、コレクションの全体のscanもできますし、効率的です。複数のコレクションは、バッチ処理で高いスループットを出すためにとても重要です。

参照


Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.

IF YOU HAVE A QUESTION, POST IT TO THE USER GROUP.

These pages are fine for comments, but for questions, your best bet will always be the MongoDB User Group.

blog comments powered by Disqus