MongoDBにおける関連(Relation)のスキーマ設計

前回、MongoDBでSNSつくるぞという記事を書いてから随分時間がたってしまいました。単に私がだらけていたということもあるのですが、一番ひっかかって時間を取られていたのが、MongoDBにおけるスキーマ設計の考え方です。

いまだに試行錯誤中ではありますが、現時点において私がこうあるべきと理解しているところをアウトプットしてみたいと思います。

1.One to Many のケース

たとえば注文と注文明細のケースを考えてみます。RDBで1対多のリレーションを設計する場合、

というように、注文明細を別テーブルにするのが普通かと思います。しかし、ドキュメント指向のMongoDBにおいては、RDBと違ってオブジェクト内に柔軟なデータ構造を実現できるため、

というように一つのCollection内にデータを埋め込んでしまうのが、パフォーマンスの点からも良しとされています。

ただし、以下の2点については注意すべきです。

まずはデータサイズの問題。MongoDBにおける1オブジェクトの最大サイズは4MBまでの制限があります。明細が巨大なデータ量になる可能性がある場合は、埋め込みにせずにCollectionを分離するのが無難です。

また、MongoDBはトップレベルのCollectionに対する検索は最適化されていますが、埋め込みデータについてはそうでもありません。注文明細を注文とは別の観点から検索する必要がある場合は、別Collectionに分けていたほうがよいでしょう。

まとめると以下の通りです。

  • 1対多のリレーションにおいてはパターン2が理想
  • データサイズが巨大になる可能性がある場合はパターン1
  • 柔軟な検索が必要となるケースでもパターン1のほうが無難

他にも留意すべき点はありますが、細かいところになるので以下の公式ドキュメントをご参照いただければと思います。
Data Modeling Considerations for MongoDB Applications日本語訳

1 to Many のケースは、シンプルなため比較的考えやすいのではないでしょうか。続いてMany to Many のケースです。

2.Many to Many のケース

ユーザが本をお気に入りにいれるケースを考えます。RDBで多対多のリレーションを設計する場合、

というように、中間テーブルを設けるのが普通かと思いますが、柔軟なデータ構造を実現できるMongoDBにおいては、中間テーブルを廃し

というような設計にすることができます。以下の資料を参考にしましたが、ここはいかにも「らしさ」がでていてるところではないかと思います。
MongoDB Schema Design • myNoSQL

ただし、One to Many のケースと同様、制限事項はあります。

多対多のこのケースでは、保持するのが他方のCollectionオブジェクトへの参照だけなので、パターン1とくらべればかなり多件数のデータを格納することができます。とはいえ、ObjectIDにしてもデータサイズは12byteかかるわけなので、あまりに件数が多すぎるとオブジェクト上限の4MBからあふれてしまいます。

1,000件程度なら問題なし、10,000件程度なら要考慮、100,000件レベルなら別Collectionといった感じになるでしょうか。Twitterでいくと、Favoriteは問題なくてFollowはアウトというイメージです。

もう一点考慮すべきは、関連(Relation)自身が独自のデータを持つケースです。お気に入りにレビューを書くことのできるケースを考えてみてください。ユーザと本のそれぞれにレビューデータを格納するのは冗長過ぎるので


というようにCollectionを分けることになります。こうなると「Favorites」はRelationというよりEntityと考えるべきかもしれません。

まとめると以下の通りです。

  • 多対多のリレーションにおいてはパターン4が理想
  • 件数がかなり多くなると考えられるケースはパターン3が無難
  • リレーション自身がデータを持つ場合はパターン3

3.まとめ

理想としてはパターン2やパターン4を使いたいところですが、色々制約もあるので、結局迷ったらRDBと同じ要領で設計してやるのが無難かなというのが、今のところの率直な感想です。

ただし、今回の説明では分散環境についての考慮がありません。スケーラビリティを実現する設計を行うためには、当然のことながらMongoDBにおけるShardingの仕組みをある程度わかっていないといけないでしょう。次回はそのあたりについて書いてみるつもりです。