Linuxを日常的に使う実験ブログ

普段RDBMSを使っている私がMongoDBを使ってみた感想と考えた設計手法

 2025-04-10

 プログラミング

最近、自身のブログ用にバックエンドとしてRust + MongoDBでヘッドレスCMSを作っています。ヘッドレスCMSといっても大げさな物ではなく、ユーザー、記事、カテゴリ、タグのCRUDができるRESTfulなAPIを提供するシンプルなシステムです。このシステムを構築するにあたりデータベースにはMongoDBを採用しました。これまでデータベースはRDBMS(Relational DataBase Managemant System)ばかり使っていたので、ドキュメント指向データベースは新鮮でしたので、使ってみた感想を記事にしてみました。

MongoDBってどんなデータベース?

MongoDBはドキュメント指向データベースというジャンルに分類されるデータベースです。リレーショナルデータベースが表計算ソフトの表のようにデータを扱うのに対して、ドキュメント指向データベースではJSONのような文字列でデータを扱うことができます。とてもザックリ説明するとJSONオブジェクトをそのまま格納することができるイメージです。実際にはMongoDBではJSON形式のデータをBSON(Binary JSON)というバイナリに変換して格納しています。

これにより、例えば以下のようなオブジェクトが別のオブジェクトを保持するようなデータも正規化することなく保持することが可能です。このことにより、リレーショナルDBではJOIN句を多用して様々なテーブルを結びつける複雑なクエリとなるようなケースでも非常にシンプルなクエリで取得することが可能です。

ドキュメント指向データベースではRDBMSとは用語が異なり概念も必ずしも1対1で対応しません。

  • フィールド : データの項目を指す。RDBMSのカラムとほぼ同じものを意味する。
  • バリュー : データの値。フィールドに対して対となる。
  • ドキュメント: フィールドとバリューのセットをまとめたもの。RDBMSのレコードに相当する
  • コレクション : ドキュメントのまとまり。RDBMSのテーブルに相当する。

ドキュメント指向データベースを選んだワケ

ブログシステムを自作したことのある方は分かると思いますが、ブログ用のシステムはシンプルで記事とユーザーが多対一、記事とカテゴリーが多対一、記事とタグの関係が多対多の関係ぐらいでRDBMSを用いて作ったとしても、それほど複雑化はしません。しかし、今回は日本語と英語の二つの言語に対応するシステムを構築することを目指したため、やや複雑化する可能性を考慮しMongoDBを使ってみようと思いました。正直に言えば、筆者の技術的興味から使ってみたかったという 点が大きいです。

RDBMSを使うことで必ず生じるデータベースの構造とプログラム上のモデル構造が不一致となるインピーダンスミスマッチについてもORMという解決策は示されているものの、ドキュメント指向データベースにより劇的な改善があるのでは?という期待もありました。

RDBMSのような設計も可能 しかし…

ドキュメント指向DBでは関連するデータは埋め込み式で同一のコレクションにまとめることができ、埋め込み方式はシンプルなクエリと読み込み速度のパフォーマンス的にも優れているので、ドキュメント指向DBでは積極的に使っていきたい設計です。一方でRDBMSのよう別のコレクションのドキュメントをIDで関連付けることも可能です。RDBMSの用語で言えば別のテーブルのレコード同士をIDで関連付けるイメージです。RDBMSの感覚のまま設計してしまうと、つい複数のコレクションのドキュメント同士をIDで連携させる設計にしてしまい勝ちですが、複数のコレクションを操作することになるので、同一ドキュメント内にオブジェクトとして埋め込んだ場合に比べて、処理が重くなり、クエリも複雑化するデメリットがあります。以下のようなメリットとデメリットを考慮し、ケースに応じて設計すべきかと思います。

IDによる複数コレクションの連携の長所と短所

  • 長所: データの更新が容易で、データの整合性が保たれる。
  • 短所: オブジェクト埋め込み方式に比べると処理が重くなる可能性がある。クエリが複雑化する。

埋め込み方式は更新に注意

一つのコレクションのドキュメントに関連するオブジェクトを埋込む「埋込み方式」はクエリが単純で済み、データ取得時にID連携に比べると高いパフォーマンスが出せるメリットがありますが、気をつけるべき点もあります。それはデータの整合性と更新処理です。

例えばブログシステムの場合、複数の記事が、同一のカテゴリーを所有しているというのはよくあるケースです。この複数の記事が所有しているカテゴリーを更新することを考える時、もしID連携していれば、マスターデータのみを更新すればそれで完了です。しかし、埋め込み方式の場合は一つの記事の所持しているカテゴリーのみを修正するとDB全体でのデータ整合性が取れなくなってしまいます。埋め込み式を採用する場合は、データの整合性を設計者やプログラマーが保持する作りにしないといけません。ここはRDBMSの思考回路になれていると、落とし穴になるかも知れません。

今回採用した設計手法

今回、自身のブログシステムを構築するにあたり、カテゴリーやタグデータに関してはカテゴリーコレクション、タグコレクションを用意し、そこにマスターデータを作成しつつ、マスターデータと同一内容のオブジェクトを記事コレクションの各ドキュメントに埋め込み式で保持させる方式の設計としました。通常であれば、マスターデータのIDと関連付ける設計とすべきですが、今回はグログシステムで最も使用する機能がCRUDのうち取得であり、記事データを取得するクエリのシンプルさとパフォーマンスとカテゴリー、タグデータ一覧の取得の容易さを第一とするため、このような作りにしました。

[IDで連携する場合]

記事コレクション

{
    "_id" : ObjectId('YYYYYYYYYYYYYY')
    "title" : "記事のタイトル",
    "author" : "Kuro",
    ...
    "category" : ObjectId('XXXXXXXXXXXXXXXX')
}

カテゴリーコレクション

{
    "_id" : ObjectId('XXXXXXXXXXXXXXXX')
    "name" : "WEB技術",
    "slug" : "web-tech"
}

[マスターデータも作成し埋込みを行なう場合]

記事コレクション

{
    "_id" : ObjectId('YYYYYYYYYYYYYY')
    "title" : "記事のタイトル",
    "author" : "Kuro",
    ...
    "category" : {
        "_id" : ObjectId('XXXXXXXXXXXXXXXX'),
        "name" : "WEB技術",
        "slug" : "web-tech"
    }
}

カテゴリーMasterコレクション

{
    "_id" : ObjectId('XXXXXXXXXXXXXXXX'),
    "name" : "WEB技術",
    "slug" : "web-tech"
}

課題としてはカテゴリーやタグデータの更新時に記事データが所持しているオブジェクトを全て更新しなければならず、データの整合性をデータベースに頼ることができないことですが、これらのデータの更新頻度が非常に低いと予想されることから、マスターデータ更新時に該当する記事データを全て更新するという重い処理が走る方法を採用しました。

ベストな設計というのはケースごとに異なると思いますが、今回のブログシステムに関してはこの設計で良かったと思っています。

MongoDBを使ってみた感想

まず、プログラム上のオブジェクトをそのままデータベースに格納できるというドキュメント指向データベースの強みはとても実感できました。シンプルに格納してあるオブジェクトを取得するとそれが、そのままプログラム上で使えるオブジェクトになるというのはORMであれこれ操作することを考慮しなくて済むので非常にストレスがなくプログラムを書くことができました。

一方で、RDBMSの作法に慣れていた分、無意識にIDで関連付ける設計にしがちで、MongoDB本来のパフォーマンスを出せるよう意識してオブジェクトの埋め込み式に修正したりと不慣れ故に戸惑う場面もありました。

まだ実運用していないので、負荷や可用性については何も検証できていません。とりあえず、開発時に触ってみた感想としては上記のような感じです。