Das richtige MongoDB-Datenmodell wählen: Eingebettete vs. Referenzierte Dokumente

Wählen Sie eingebettete oder referenzierte MongoDB-Dokumente basierend auf Zugriffsmustern, Wachstum, Konsistenz und Aktualisierungsverhalten.

Das richtige MongoDB-Datenmodell wählen: Eingebettete vs. referenzierte Dokumente

Bei der MongoDB-Datenmodellierung geht es meist um eine praktische Frage: Sollen zusammenhängende Daten im selben Dokument leben, oder soll ein Dokument auf ein anderes verweisen? Diese Entscheidung beeinflusst die Lesegeschwindigkeit, die Aktualisierungskosten, das Dokumentwachstum und den Aufwand für die Konsistenzsicherung in Ihrer Anwendung.

Dieser Leitfaden taucht tief in die Abwägungen zwischen dem Einbetten von Dokumenten in ein übergeordnetes Dokument und dem Referenzieren verwandter Dokumente über verschiedene Sammlungen hinweg ein. Das Verständnis, wann und wie diese Techniken anzuwenden sind, ermöglicht es Ihnen, effiziente, leistungsstarke MongoDB-Schemata zu entwerfen, die auf die spezifischen Zugriffsmuster Ihrer Anwendung zugeschnitten sind.

Grundlegendes zu MongoDB-Datenmodellierungsstrategien

MongoDB organisiert Daten in Dokumenten (ähnlich JSON-Objekten), die in Sammlungen gespeichert werden. Beziehungen zwischen diesen Dokumenten können mit zwei Kernmustern modelliert werden:

  1. Einbettung (Denormalisierung): Speichern verwandter Daten direkt im übergeordneten Dokument.
  2. Referenzierung (Normalisierung): Speichern nur eines Verweises (z. B. einer _id) auf das verwandte Dokument in einer anderen Sammlung, ähnlich einem Fremdschlüssel.

1. Das Einbettungsmuster (Denormalisierung)

Bei der Einbettung wird ein Dokument direkt in ein anderes eingefügt. Diese Technik wird in MongoDB bevorzugt, wenn Datenbeziehungen eins-zu-wenige sind oder wenn die zugehörigen Daten häufig zusammen mit dem übergeordneten Dokument abgerufen werden.

Wann Einbettung verwendet werden sollte

Verwenden Sie das Einbettungsmuster, wenn:

  • Daten gemeinsam abgerufen werden: Wenn Sie beim Abfragen des übergeordneten Dokuments fast immer die zugehörigen Daten benötigen, minimiert die Einbettung die Anzahl der Datenbankoperationen, die zum Abrufen des vollständigen Informationssatzes erforderlich sind.
  • Eins-zu-wenige-Beziehungen: Ideal für Beziehungen, bei denen das Array eingebetteter Dokumente relativ klein und vorhersagbar bleibt (z. B. die letzten 10 Anmeldeaktivitäten eines Benutzers oder die Positionen einer Bestellung).
  • Datenkonsistenz ist entscheidend: Eingebettete Daten sind von Natur aus konsistent, da sie sich in einem einzigen Dokument befinden, was die von MongoDBs Ein-Dokument-ACID-Transaktionen bereitgestellten Atomaritätsgarantien vereinfacht.

Beispiel für Einbettung

Betrachten Sie ein Produkt und seine Bewertungen. Wenn Bewertungen häufig zusammen mit dem Produkt abgerufen werden und die Gesamtzahl der Bewertungen überschaubar ist:

// Produktsammlungsdokument
{
  "_id": ObjectId("..."),
  "name": "Hochleistungs-SSD",
  "preis": 129.99,
  "bewertungen": [
    {
      "benutzer": "Alice",
      "bewertung": 5,
      "kommentar": "Schnellste Festplatte aller Zeiten!"
    },
    {
      "benutzer": "Bob",
      "bewertung": 4,
      "kommentar": "Tolles Preis-Leistungs-Verhältnis."
    }
  ]
}

Nachteile der Einbettung

  1. Dokumentgrößenbeschränkungen: MongoDB-Dokumente haben eine maximale Größenbeschränkung von 16 MB. Wenn das Array eingebetteter Dokumente ohne klare Grenze wächst, benötigen Sie möglicherweise irgendwann Referenzierung oder Bucketing.
  2. Aktualisierungsaufwand: Große eingebettete Arrays können Aktualisierungen teurer machen und die Konkurrenz um das übergeordnete Dokument erhöhen.
  3. Datenduplizierung: Wenn die eingebetteten Daten unabhängig vom übergeordneten Element geteilt oder angezeigt werden müssen, riskieren Sie Datenduplizierung und eventuelle Konsistenzprobleme, wenn Aktualisierungen nicht über alle Kopien hinweg synchronisiert werden.

2. Das Referenzierungsmuster (Normalisierung)

Die Referenzierung ahmt das Konzept von Fremdschlüsseln in relationalen Datenbanken nach. Anstatt die zugehörigen Daten einzubetten, speichern Sie die _id oder einen anderen stabilen Bezeichner für das verwandte Dokument. Dies erfordert oft eine zweite Abfrage, eine $lookup-Aggregationsstufe oder einen anwendungsseitigen Join, um die zugehörigen Daten abzurufen.

Wann Referenzierung verwendet werden sollte

Verwenden Sie das Referenzierungsmuster, wenn:

  • Eins-zu-Viele- oder Viele-zu-Viele-Beziehungen: Wenn eine Seite der Beziehung unbegrenzt wachsen kann (z. B. die Anzahl der Kommentare zu einem Blogbeitrag oder Benutzer, die zu vielen Gruppen gehören).
  • Daten werden von mehreren übergeordneten Elementen gemeinsam genutzt: Wenn die zugehörige Datenentität unabhängig aktualisiert und von mehreren anderen Dokumenten abgerufen werden muss (z. B. ein Kategorie-Dokument, das von vielen Produkt-Dokumenten verwendet wird).
  • Große Datensätze: Wenn die Einbettung das 16-MB-Dokumentgrößenlimit verletzen würde.

Arten der Referenzierung

A. Manuelle Referenzen (Anwendungsseitige Joins)

Speichern der _id im übergeordneten Dokument:

// Autorensammlung
{
  "_id": ObjectId("author123"),
  "name": "Jane Doe"
}

// Buchsammlung
{
  "_id": ObjectId("book456"),
  "titel": "Datenmodellierung 101",
  "autor_id": ObjectId("author123") // Referenz
}

Um den Namen des Autors abzurufen, führen Sie zwei Abfragen durch oder verwenden $lookup:

// Beispiel mit $lookup im Aggregationsframework
db.buecher.aggregate([
  { $match: { titel: "Datenmodellierung 101" } },
  {
    $lookup: {
      from: "autoren",          // Sammlung, die gejoint werden soll
      localField: "autor_id",   // Feld aus den Eingabedokumenten (Bücher)
      foreignField: "_id",      // Feld aus den Dokumenten der 'from'-Sammlung (Autoren)
      as: "autorendetails"
    }
  }
]);

B. Bidirektionale Referenzen

Für zweiseitige Beziehungen können Sie auch das übergeordnete Element im untergeordneten Dokument referenzieren. Dies erleichtert das Durchlaufen der Beziehung in beide Richtungen, erhöht jedoch den Schreibaufwand, da Aktualisierungen an zwei Stellen erfolgen müssen.

Nachteile der Referenzierung

  1. Erhöhte Abfragekomplexität: Das Abrufen vollständig denormalisierter Daten erfordert Joins (entweder über Anwendungscode oder MongoDBs $lookup), was langsamer sein kann als ein einzelner eingebetteter Lesevorgang.
  2. Konsistenzverwaltung: Wenn Sie ausgewählte Felder aus einem referenzierten Dokument duplizieren, z. B. den Anzeigenamen eines Autors in einem Bucheintrag, müssen Sie diese Kopien aktualisieren oder vorübergehende Veraltung in Kauf nehmen.

Zusammenfassung: Die richtige Wahl treffen

Die Entscheidung zwischen Einbettung und Referenzierung dreht sich um Zugriffsmuster. Fragen Sie sich: Wie oft werden diese zugehörigen Daten abgerufen? Wie oft ändern sie sich? Sind sie klein oder potenziell massiv?

Merkmal / Überlegung Einbettung (Denormalisierung) Referenzierung (Normalisierung)
Leseleistung Hervorragend (Einzelne Abfrage) Gut bis Mittelmäßig (Erfordert Joins)
Schreibleistung Kann für große oder heiße übergeordnete Dokumente kostspielig sein Oft einfacher für unabhängige Dokumente
Datengrößenbeschränkung Begrenzt durch das 16-MB-Dokumentlimit Vermeidet ein riesiges übergeordnetes Dokument, erfordert aber dennoch sorgfältig entworfene Indizes und Abfragegrenzen
Beziehungstyp Eins-zu-Wenige Eins-zu-Viele, Viele-zu-Viele
Datenkonsistenz Hoch (Atomare Schreibvorgänge) Manuell verwaltet (Potenzielle Veraltung)

Best-Practice-Tipp: Mit Einbettung beginnen, später umstellen

Eine gängige und effektive Strategie ist es, mit der Einbettung von Daten zu beginnen, von denen Sie wissen, dass Sie sie häufig gemeinsam lesen. Dies optimiert den häufigsten Fall. Wenn Sie später auf Leistungsengpässe aufgrund von großem Dokumentwachstum oder übermäßiger Aktualisierungskomplexität stoßen, können Sie diese spezifischen Daten in eine eigene Sammlung umstellen und auf Referenzierung wechseln.

Fazit

MongoDB-Schemata funktionieren am besten, wenn sie Ihren tatsächlichen Abfragen entsprechen. Betten Sie Daten ein, die Sie gemeinsam lesen und die Sie begrenzt halten können. Referenzieren Sie Daten, die unabhängig wachsen, von vielen übergeordneten Elementen gemeinsam genutzt werden oder sich nach einem eigenen Zeitplan ändern. Bevor Sie sich für ein Modell entscheiden, notieren Sie Ihre wichtigsten Lese- und Schreibvorgänge und testen Sie diese Pfade dann mit realistischen Dokumentgrößen.