MongoDBとは?特徴、ドキュメント設計と導入の判断基準

MongoDBは、データを主にリレーショナルテーブルとして整理するのではなく、ドキュメントとして保存するデータベースです。さまざまな属性を持つ製品カタログやネストされたユーザープロファイルなど、共に読まれることの多いデータで利用を検討できますが、スキーマ設計とパフォーマンス確認は行う必要があります。

このガイドでは、MongoDBのストレージモデル、機能、ユースケース、および採用基準について説明します。より広い視野については、NoSQLモデルとリレーショナルデータベースの比較を参照してください。

MongoDBはどのようにデータを保存するのですか?

ドキュメントはフィールドと値のペアで構成されます。コレクションはドキュメントの論理的なグループであり、データベースは製品、注文、顧客など、目的の異なるコレクションを含むことができます。

MongoDBはドキュメントをBSON(Binary JSON)として保存します。BSONはJSON(JavaScript Object Notation)構造をバイナリ形式で表現し、日付やObjectIdの値など、追加の型もサポートします。したがって、MongoDBを単にJSONテキストファイルを保存するストアとして理解すべきではありません。ドキュメント参照では、型と識別子のルールについて説明しています。

この架空の製品JSONは、ドキュメントの形状を示しています。標準的なコレクションでは、_idがドキュメントを識別します。例では読みやすい文字列を使用していますが、アプリケーションでは依然として識別子の形式と重複処理のルールが必要です。

{
  "_id": "book-101",
  "name": "Database Basics",
  "category": "books",
  "price_krw": 24000,
  "attributes": {
    "author": "Mina Kim",
    "pages": 320
  },
  "tags": [
    "database",
    "beginner"
  ]
}

attributesはネストされたオブジェクトで、tagsは配列です。本には著者やページ数の属性があり、電子機器にはバッテリーの情報があります。価格タイプ、必須フィールド、欠損値に関する共通ルールを保持してください。

主な機能とその条件

柔軟なスキーマとバリデーション

スキーマはフィールド、型、必須条件を定義します。MongoDBでは、デフォルトでコレクション内のすべてのドキュメントが同一のフィールドを持つ必要はありません。その柔軟性はスキーマ設計を不要にするものではありません。

スキーマ検証 は、許可される型や値の範囲を制約できます。混在する数値/文字列の価格や不一致の日付表現を防ぐために、アプリケーション側のチェックとデータベースの検証を調整してください。既存データにルールを追加する前に、基準に合わないドキュメントを特定し、更新方法を決定してください。

クエリ、集計、および関連データ

MongoDB のクエリは、SQL 文をそのまま受け入れるのではなく、フィールド条件と演算子を使用します。使用している言語のドライバやシェルで使われる構文を学んでください。ドット表記でネストされたフィールドにアクセスでき、配列条件もクエリで表現できます。

集約パイプラインは、フィルタリング、グループ化、計算などのステージを通じてドキュメントを処理します。例えば、キャンセルされていない注文アイテムをフィルタリングし、製品ごとの数量を合計することができます。集約パイプラインガイドは、ステージの動作方法について説明しています。

$lookupは別のコレクションへの左外部結合を提供するため、「MongoDBには結合がない」というのは正確ではありません。$lookupのサポートとパフォーマンス条件を確認してください。リレーショナルの外部キー制約に関連する参照整合性の強制が自動的に課されると仮定しないでください。

インデックスとパフォーマンス

インデックスは、一致するドキュメントを見つけるために調べるデータ量を減らすことができるデータ構造です。単一フィールドのインデックスと複合インデックスは異なるアクセスパターンに対応します。インデックスのドキュメントは、クエリの利点と書き込みコストの両方を説明しています。

インデックスが増えるほど、ストレージが必要になり、書き込み時にそれらを維持する作業も増えます。パフォーマンスはドキュメントのサイズ、読み書きの比率、メモリ、ディスク、ネットワークにも依存します。NoSQLが本質的に高速であると仮定せずに、実行計画と現実的な負荷を用いて代表的なクエリを評価してください。

トランザクションと読み書きの保証

単一ドキュメントの更新はアトミックです:その更新を部分的に適用することはありません。ドキュメント間で一緒に成功または失敗する必要がある変更については、レプリカセットやシャードクラスタでのマルチドキュメントトランザクションを検討してください。スタンドアロンサーバーで同じ機能が存在すると仮定しないでください。

ACIDは、原子性(Atomicity)、一貫性(Consistency)、独立性(Isolation)、永続性(Durability)を意味します。トランザクションのドキュメントで、デプロイ形態および設定条件を確認してください。注文作成と在庫削減が別々のドキュメントに影響する場合は、トランザクションの境界、同時リクエストの処理、およびリトライ動作を定義してください。

リードコンサーン(read concern)は読み取りで参照できるデータの状態を制御し、ライトコンサーン(write concern)は書き込み完了を確認する水準を制御します。支払い完了を利用者に伝える前に必要な保証を定め、レプリカの古い値を許容できる操作と区別してください。

関連データは埋め込むべきですか、それとも参照すべきですか?

埋め込みは関連データを1つのドキュメント内に配置します。参照は別のドキュメントの識別子へのリンクを作ります。モデリングガイドは、一緒にアクセスされるものは一緒に保存することを推奨します。埋め込みと参照の比較ガイドは、関係性と更新に関するトレードオフを説明しています。

意思決定領域 埋め込みを考慮する場合 参照を考慮する場合
読み取り 親および関連の詳細は通常一緒に読み取られる 関連の詳細は独立して読み取られる
更新 値は1つの更新単位に属する 共有情報は独立して変更される
サイズ グループには制限されたサイズと数がある 項目は増え続けるか、大きい
例 注文の配送先住所および購入した商品の名前 完全なレビュー履歴または共有された顧客プロフィール

配達先住所を注文にコピーすると、その履歴的な意味を保持することができます。顧客の現在の連絡先情報をすべての注文にコピーすると、それらの情報が変更されたときに維持するためのコピーが作成されます。まず、歴史的事実と常に最新である必要がある値を区別してください。

通常のBSONドキュメントの最大サイズは16 MiB(メビバイト)です。すべてのコメントやログエントリを1つの配列に追加すると、その制限に達したり、更新コストが増加したりする可能性があります。別々のコレクション、参照、またはドキュメントサイズの制限を考慮した境界付きグループを使用してください。

どのユースケースが検討に値しますか?

これは、業界ラベルに基づく保証ではなく、ドキュメントモデルを評価する理由です。実際の読み取りや更新が選択を左右するべきです。

ユースケース ドキュメントを検討する理由 確認すべき条件
製品カタログ 異なる属性とネストされた製品詳細 共有フィールドの検証およびフィルタリング/ソート用インデックス
プロフィールとコンテンツ 一画面での関連プロパティの読み取り アクセス権限、大規模配列、重複データ
イベントおよびデバイス記録 イベント固有の構造と追加プロパティ 保持、デバイス/時間クエリ、および容量の成長
注文 注文の状態のスナップショット 在庫/支払いの正確性、同時更新、およびトランザクション

柔軟なクロステーブルレポート、共有関係および制約、または複雑な多対多の更新が中心的な要件である場合は、リレーショナルデータベースと比較してください。JSONを単に保存するだけでは、既存のデータベースを置き換える理由にはなりません。ドキュメントモデルが重要なクエリや運用負荷を改善するかどうかを確認してください。

小さな挿入とクエリの例

以下はMongoDB Shellのmongoshを使う例です。シェルをインストールし、対象データベースの読み取り・書き込み・インデックス作成権限を持つアカウントで、本番とは別の練習用環境に接続してください。接続手順はMongoDB Shellのドキュメントを参照してください。

この例では、mongodb_intro_demoデータベースのproduct_catalogに1つの製品を挿入します。練習環境でこの名前のコレクションを空の状態で開始し、コマンドを実行する前に接続がその環境を対象としていることを確認してください。

db = db.getSiblingDB("mongodb_intro_demo");

db.product_catalog.insertMany([
{
  "_id": "book-101",
  "name": "Database Basics",
  "category": "books",
  "price_krw": 24000,
  "attributes": {
    "author": "Mina Kim",
    "pages": 320
  },
  "tags": [
    "database",
    "beginner"
  ]
}
]);

insertMany() はドキュメントを挿入します。標準コレクションで同じ _id を繰り返すと、重複キーエラーが発生します。繰り返し練習する場合は、新しい練習用データベースを使用するか、追加したサンプルドキュメントだけを明示的に識別して処理してください。そのエラーを解決するために既存のコレクションを削除しないでください。

このクエリは、カテゴリがbooksで価格が30,000韓国ウォン未満の商品を検索し、識別子・名前・価格を返して価格の昇順に並べます。サンプルデータだけがある場合はbook-101が条件に一致します。

db.product_catalog.find(
  { category: "books", price_krw: { $lt: 30000 } },
  { _id: 1, name: 1, price_krw: 1 }
).sort({ price_krw: 1 });

$ltは「未満」を意味します。2番目のオブジェクトは返すフィールドを指定するプロジェクションです。sort()の1は昇順を表します。find()のリファレンスではフィルターとプロジェクションを説明しています。

頻繁にカテゴリ別の価格フィルターや並べ替えを行う場合は、この複合インデックスを検討してください。実際の分布や他のクエリを確認してから、createIndex() を本番で適用してください。

db.product_catalog.createIndex(
  { category: 1, price_krw: 1 }
);

以下のコマンドはクエリを実行し、ドキュメントを変更せずに実行統計を報告します。explain("executionStats") の結果では、選択されたプラン、キーと確認されたドキュメント、返されたドキュメントを確認してください。小さなコレクションではコレクションスキャンが使用されることがあります。単にインデックスを作成するだけでは速度向上を示すことはできません。

db.product_catalog.find(
  { category: "books", price_krw: { $lt: 30000 } }
).sort({ price_krw: 1 }).explain("executionStats");

レプリケーションとシャーディングは異なる役割を持ちます

レプリカセットは、同じデータを保持するサーバーのグループです。通常、プライマリが書き込みを受け入れ、セカンダリが変更を複製します。適格なメンバーは、障害発生後に新しいプライマリを選出できます。レプリケーションガイドでは、選挙と非同期レプリケーションについて説明しています。

フェイルオーバーは一時的な書き込み遅延やエラーを引き起こす可能性があり、セカンダリの読み取りはレプリケーション遅延を反映する場合があります。レプリケーションは、無条件に継続的なサービスを保証したり、すべてのデータ損失を防ぐものではありません。アプリケーションと共にリトライ、重複リクエスト処理、および必要な読み書き保証を設計してください。

シャーディングは、水平スケーリングのためにサーバー間でデータを分散します。シャードキーには、データの分散とクエリルーティングに使用されるフィールドが含まれます。集中したキーや、すべてのシャードを訪問する必要があるクエリは、その利点を制限する可能性があります。シャーディングガイドでは、キーの選択と運用の複雑さについて説明しています。

レプリケーションとシャーディングは一緒に使用できますが、異なる目的を持ちます。データが増える可能性があるからという理由だけでシャーディングを導入する前に、モデル、インデックス、および現在のボトルネックを確認してください。その追加の複雑さとコストを受け入れる理由が必要です。

MongoDBを採用する前に何を確認すべきですか?

  1. 重要な操作を定義します。製品詳細、カテゴリ価格検索、注文作成のフィルター、並べ替え、更新単位を指定します。実際のリレーショナルおよびドキュメントの実装を比較します。
  2. モデルと正確性を確認します。埋め込みまたは参照、ドキュメントサイズ、検証、インデックス、トランザクション、および即時読み取り要件を決定します。
  3. 代表的な負荷を比較します。現実的なデータとリクエスト分布でレイテンシ、スループット、エラーを測定します。バージョン、ハードウェアおよび設定を記録し、障害および回復シナリオを検討します。
  4. 運用モデルを選択します。セルフマネジメントかマネージドサービスかを決定し、バックアップ、復元、監視、アップグレード、サポートの責任を割り当てます。
  5. 移行と元の状態への復帰ルートを準備します。識別子/タイプの変換、欠落データの確認、カットオーバー中の書き込み、既存ストアに戻す条件を定義します。

MongoDB Atlasは、セルフマネージドの展開とは異なる運用責任の分担を持つマネージドクラウドサービスです。必要な機能や展開オプションを確認するためにAtlasドキュメントを使用してください。ストレージ、バックアップ、ネットワーキング、運用作業、インスタンスコストも含めます。

本番環境では、展開に適したセキュリティガイダンスを用いて、最小権限アカウント、暗号化接続、ネットワーク制限、および資格情報管理を確認してください。偶発的な削除はレプリカに伝播する可能性があるため、レプリケーションはバックアップの代わりにはなりません。バックアップ手法ガイダンスに従い、復元が実際に機能することを確認してください。