LoginSignup
146
136

More than 3 years have passed since last update.

MongoDBのスキーマデザインする時の3つのポイント

Last updated at Posted at 2015-02-02

SnapDishではDBにMongoDBを使っている。バージョンが1.4ぐらいの時から使っていて、色々と試行錯誤をしながら使ってきた。不勉強もあり今だに負の遺産が残る中、現在開発しているサービスでMongoDBをどのように利用したらよいか再度考えてみることに。

振り返るとやはりスキーマデザインが常に一番悩むところ。ただ、悩んでいる時間が惜しいので、決断を早くするために、スキーマの設計をするにあたり一定の考え方を設けることに。

それで、できるだけシンプルにということを念頭に、今更ながらだけど 3つのポイント にまとめてみることに。

様々な意見や議論のある領域だと思うので、これを読んだ方で、「この場合はどうなる?」や「ツッコミ」などあれば、ぜひコメンントや意見を頂きたところ。今後の参考したい。また、「その考え方はこういうことだよ」という、適切な表現や言葉があれば教授してもらいたい。

まず、、、

SnapDishは料理サービスなので、料理詳細情報をどう取得するかを題材に書いてみることにした。

※Sharding環境に関しては全く考慮しいない

料理詳細ページ生成のためのスキーマイメージ

# 利用するコレクション
# dish.collection - 料理情報を保持する
# dish_comment.collection - 料理情報に対するコメント
# user.collection - ユーザー情報を保持する

# 料理写真詳細コレクションイメージ
db.dish.collection = {
 "_id": ObjectId(),
 "usr_id": db.user.collection._id,
 # 以下の様にしたいところだが、apiなどでデータを返す場合返す時に、情報を再取得してデータを返す
 #"usr": {
 #  "_id": ObjectId(), # ユーザーのObjectId
 #  "unm": "名前",
 #  "anm": "アカウント名",
 #  "img": {画像URL},
 # },
 "nm": "料理名",
 "rcp": {レシピ}, # 無制限に肥大しないので自由にフィールド設定ができる。MongoDBの得意なところ
 "img": {画像URL},
 "tag": [タグ,,,],
 "tm": datetime,
 # 以下のようにしたいところだが、別コレクションにする
 # "cmts": [{コメント},] 
 ...
}

# 料理写真詳細へのコメントコレクションイメージ
# コメントは無制限に増えるので、別のコレクションにしたほうがよい
db.dish_comment.collection = {
 "_id": ObjectId(),
 "dsh_id": dish.collection._id,
 "usr_id": user.collection._id, 
 "txt": "コメント",
 "tm": datetime,
 "mt": ["メンションされたusr_id",,,],
 ...
}

# ユーザー情報コレクションイメージ
db.user.collection = 
 "_id": ObjectId(), # ユーザーのObjectId
 "unm": "名前",
 "anm": "アカウント名",
 "img": {画像URL},
 ...
} 

上記のスキーマで料理の詳細データを取得する際以下の流れになる

  • DBアクセス 料理データを_idで取得
  • DBアクセス dsh_idを参照しているコメントデータを取得
  • 料理データとコメントデーターからuser._idを抽出
  • DBアクセス user._idを使ってユーザーのデータを取得
  • dish.collection.data と dish_comment.collection.data とユーザーデータをマージし、レスポンスデータを生成

3つのポイント

無制限に増えるフィールドは別コレクションにする

メモリーにあるデータの取得は早いので別コレクションでも全く問題はない。また、上記の場合 DBへアクセス は3回になるが、メモリキャッシュをうまく利用すると DBアクセス 回数は容易に減らせ、場合によってはレスポンスの向上はできる。なので、ドキュメントが肥大化する場合、コレクションを分けて、アプリケーション側で処理をしても問題はない。

フィールド名は短くても構わない

フィールド名も要領を食うので、フィールド名はなるべくコンパクトにする。実際、ある程度意味が推測できればそこまで問題にはなっていない。直感的にわからないという重大な課題はあるが、最終的には慣れる(強気でいまのところ通している)。ただし、仕事の現場は、場合によっては引き継ぎなどもあるので スキーマのフィールド名の説明をしたドキュメント はもちろんしっかり残しておく必要はある。

MongoDBはシンプルなクエリーでデータを取得するためのストレージとして使う

集計や検索は別のサービスに任せるか、書き込み時に非同期で別に渡すなど工夫をするとよい。単純な条件でデータを取得することにを一番重視し、それ以外のことは、別途考えるか別に任せる。やはりMongoDBは Slow-Queryが最大の地雷 で、設計段階でその可能性を軽減するということが大切だと考える。

ODM(object document mapper)を必ず使おう

非同期I/O non-blocking処理を行いたい場合(使わないとあまり恩恵はないので使ったほうが良い)は、 μmonogo を使って、document format定義を事前に行うと良い。write operationの場合は、必ず μmongoDocument Class を使って定義をすれば、ソフトウエア側である程度データ型の管理ができる。ざっくりプロトタイピング的に作る時も、書式は結構簡単なので、利用をすすめたい。このお作法自体どうよ、とツッコミも多くもらうが、ある程度のサービスでは耐えられると思う。

また、古くからmongoを使っていて、ドキュメントデータのフォーマットにばらつきが出ている場合、データのマイニングや集計などをする時には、ODMは、結構重要。

参照: μmongo (umongo) を使ってmongodb documentデータをcsv へコンバートしてみる

まとめ

3つのポイントを押さえておけば、スキーマ設計での苦悩が軽減されると考えている。他にもっとケアーしなければならないことがあるかもしれないが、みなさんがどう考えるか聞きたいところ。あと、MongoDBを利用してSnapDishのようなサービスを開発する際は参考にしてもらえたらと。

関連記事

146
136
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
146
136