NoSQL: Dokumentenorientierte Datenbanken (CouchDB, MongoDB)
DocumentStores oder dokumentenorientierte Datenbanken (Document-oriented Databases) dienen, wie der Name bereits sagt, vor allem dem Speichern von Dokumenten. Über dies hinaus können sich natürlich auch weitere sinnvolle Einsatzzwecke für andere Datentypen ergeben.
Um der Definition von DocumentStores näher zu kommen, wollen wir uns als Beispiel ansehen, wie wir ein Dokument in einem DocumentStore speichern können. Als Beispiel nehmen wir diesen Blog-Eintrag:
- Titel: Algorithmen zur Bestimmung von Ähnlichkeiten
- Autor: Stefan Koch
- URL: 2011/04/04/algorithmen-zur-bestimmung-von-aehnlichkeiten
- Text: Zur Ermittlung der Ähnlichkeit zweier Mengen oder Messreihen gibt es verschiedene Algorithmen …
Der Vorteil von DocumentStores gegenüber relationalen Datenbanken besteht in der wenig stärkeren Struktur. So muss man sich nicht um relationen zwischen verschiedenen Tabellen kümmern, wenn man in einem Feld eine Liste (ein Array) speichern möchte. Außerdem kann man ohne Probleme nachträglich bei einem Eintrag neue Felder hinzufügen, die bisher nicht existiert haben, oder andere weglassen, die bei manchen Einträgen verwendet wurden.
Nehmen wir als weiteres Beispiel diesen Blog-Eintrag. Dabei muss man sich klarmachen, dass ich Serien erst für diesen Blog-Eintrag hinzugefügt habe, da ich zuvor nicht an solch eine Möglichkeit dachte. Bei einer relationalen Datenbank müsste man nun die Struktur der Datenbank anpassen und NULL zulassen, einen DocumentStore können wir ohne Änderungen weiterverwenden und unserem neuen Datensatz einfach das zusätzliche Feld hinzufügen. Verschiedene Einträge in einem DocumentStore dürfen auch verschiedene Felder besitzen.
- Titel: „NoSQL: Document Store (CouchDB, MongoDB)“
- Autor: Stefan Koch
- URL: 2011/05/08/nosql-document-store-couchdb-mongodb
- Serie: Nicht-relationale Datenbanken
- Einleitung: Jeden zweiten Mittwoch erkläre ich in der NoSQL-Reihe ein Datenbanksystem …
- Text: DocumentStores oder dokumentenorientierte Datenbanken (Document-oriented Databases) dienen …
Relationen in dokumentenbasierten Datenbanken
Natürlich kann man auch in dokumentenbasierten Datenbanken Relationen umsetzen und je nach Art kann dies auch sinnvoll sein. Genau wie man mit relationalen Datenbanken durchaus andere Schemata umsetzt, kann man auch mit dokumentenbasierten Datenbanken andere Schemata umsetzen, solange der zusätzliche Berechnungsaufwand nicht zu groß wird. Erreicht man allerdings eine große Datenmenge von relationalen Daten sollte man sich lieber überlegen, die Daten in zwei Datenbanksysteme aufzuteilen.
Eingebettetes Dokument
Im Kern gibt es bei DocumentStores zwei verschiedene Methoden, um Relationen umzusetzen. Bei der ersten und einfacheren wird ein Dokument einfach an das aktuelle Dokument anhängt, als ein neuer Unterpunkt. So könnten wir unserem Blog-Eintrag zum Beispiel noch ein Artikelbild mit eigener Bildunterschrift als Unterdokument hinzufügen:
title: "NoSQL: Document Store (CouchDB, MongoDB)"
header_image:
image: "/images/document-stores-panorama.jpg"
subtitle: "DocumentStores erweisen sich oft als sehr nützlich"
text: "DocumentStores oder dokumentenorientierte Datenbanken …"
Diese Methode bietet sich dann an, wenn Daten bei jedem Auslesen des Eintrags benötigt werden oder nicht besonders groß sind. Man muss bedenken, dass DocumentStores bei jedem Zugriff den kompletten Eintrag auslesen und man deshalb eingebettet keine Inhalte speichern sollten, die viel Platz erfodern, aber nie benötigt werden. Außerdem eignen sich eingebettete Dokumente nur dann, wenn die Daten nicht mehreren Hauptdokumenten gleichzeitig zugeordnet werden.
Ein weiteres Beispiel für eingebettete Dokumente, welches von MongoDB angeführt wird, sind Kommentare in einem Blog-System. Auch diese werden üblicherweise bei jedem Aufruf des Dokuments benötigt (auf der Startseite zum Zählen und beim Eintrag selbst zum Anzeigen der Kommentare). Unter einem Punkt comments
würde dann eine Liste mit allen Unterdokumenten gespeichert:
title: "NoSQL: Document Store (CouchDB, MongoDB)"
text: "DocumentStores oder dokumentenorientierte Datenbanken …"
comments:
[
{
author: Besucher,
mail: besucher@example.com,
date: 2011-05-20,
text: Ich verwende selbst bereits DocumentStores.
},
{
author: Besucher 2,
date: 2011-05-21,
text: Ich kannte DocumentStores noch nicht.
}
]
Referenz auf ein eigenes Dokument
Will man die Daten nicht jedes Mal auslesen oder sie mehreren Personen gleichzeitig zuordnen (z.B. Artikel in einem Online-Shop, die verschiedene Benutzer gut finden), muss man auf Referenzen zwischen den Dokumenten ausweichen. Jedoch muss man sich fragen, ob ab einem bestimmten Punkt nicht doch relationale Datenbanken für das eigene Projekt sinnvoller wären, da diese auf solche Probleme optimiert sind.
Die Referenzierung von Fremddokumenten funktioniert wie bei relationalen Datenbanken über die eindeutige ID der Einträge. Im Gegensatz zu relationalen Datenbanken, wo verschidene Tabellen existieren, gibt es bei DocumentStores hingegen für jedes einzelne Objekt eine eigene ID. Eine Typ-Unterscheidung (wie dies bei relationalen Datenbanken nach Tabellen stattfindet) existiert in DocumentStores nicht, es gibt nur einen großen Speicher mit allen Dokumenten und eindeutiger ID.
Möchte man nun von einem Kunden auf ein Produkt referenzieren, muss man die ID des Produkts herausfinden und dem Kunden dann diese ID zu einer Liste likes
hinzufügen.
{
_id: "1"
name: "Kunde 1"
likes:
[2, 3]
},
{
_id: "2"
name: "Produkt 1"
price: 100
},
{
_id: "3"
name: "Produkt 2"
price: 150
}
Wenn die Liste zu groß würde, kann man alternativ auch ein Relationsdokument (ähnlich der n:m-Tabellen bei relationalen Datenbanken) erstellen, doch dies würde das Konzept von DocumentStores vollkommen ad absurdum führen und eine relationale Datenbank ist in solch einem Fall anzuraten.
Ein Vorteil von DocumentStores gegenüber relationalen Datenbanken besteht bei solchen Referenzen, dass man jedes beliebige Objekt mögen und eine einfache Referenz erstellen kann, während man bei relationalen Datenbanken dann in irgendeiner Form den Tabellennamen hinzuziehen muss. Nehmen wir als Beispiel, dass unser Kunde 1 nicht nur Produkte mag, sondern auch unseren Kunden 2. In einem RDBMS müsste man nun in einer Referenztabelle angeben, aus welcher Tabelle die ID stammt, die unser Kunde mag oder mehrere Referenztabellen (likes_item
, likes_person
) anlegen. Bei dokumentenbasierten Datenbanken gibt man einfach eine weitere ID an:
{
_id: "1"
name: "Kunde 1"
likes:
[2, 4]
},
{
_id: "2"
name: "Produkt 1"
price: 100
},
{
_id: "3"
name: "Produkt 2"
price: 150
},
{
_id: "4"
name: "Kunde 2"
}
Ein neuer Nachteil ist dann jedoch, dass man die Fälle in der Programmiersprache wieder unterscheiden und evtl. sogar darauf achten muss, ob überhaupt ein Feld name
existiert, das man ausgeben muss. Zu diesem Zweck müsste man vermutlich ein Feld type
einführen, dessen Sinnhaftigkeit in solch einer Verwendung (zur Unterscheidung von Referenzen) auch wieder eher fragwürdig ist.
Map/Reduce
Das Auslesen der Datensätze erfolgt üblicherweise über einen Map/Reduce-Algorithmus. Bei MongoDB entfällt dieser (zumindest aus Nutzersicht, das Backend kenne ich nicht) bei einfachen Abfragen allerdings zugunsten einer Liste mit gesuchten Werten.
Map
Der erste Schritt des Map/Reduce-Verfahrens ist das Mapping. Dabei werden alle in frage kommenden Datensätze an eine Mapping-Funktion übergeben, welche dann entscheidet, was mit diesem Datensatz geschehen soll. Die Ergebnisse solch eines Mapping-Vorgangs werden anschließend in irgendeiner Form gespeichert, bei CouchDB in Form eines B-Tree-Indexes, bei MongoDB ist eine Möglichkeit die Speicherung als neue Collection (Collections kann man als einzelne unabhängige DocumentStores betrachten).
Reduce
Mit einem anschließenden Reduce-Schritt können die Ergebnisse gruppiert werden. Reduce gibt nur einen Datensatz als Ergebnis zurück (es reduziert die Eingabemenge auf ein Ergebnis). Mit dem Reduce-Schritt kann man alle möglichen Dinge anstellen, das CouchDB-Wiki gibt einige Beispiele:
- Dokumente mit bestimmter Eigenschaft zählen
- Unique-Werte ausgeben
- die wichtigsten Tags errechnen
Fazit
DocumentStores eignen sich besonders gut, wenn man nicht stark vernetzte Daten speichern muss und die einzelnen Daten dann auch noch unterschiedliche Eigenschaften haben, sodass es unmöglich wäre, eine gemeinsame Struktur anzulegen, die allen Daten gerecht wird.
Das Map/Reduce-Verfahren zur Erstellung von Ergebnismengen eignet sich auch, wenn man häufig kompliziertere Berechnungen für Ausgaben anstellt und dank Mappings beim DocumentStore einen Index für die komplexe Operation anlegen kann. Schaut man sich allerdings die Implementierung von MongoDB mit neuen Collections an, dann wäre wohl ein temporärer Speicher in einem anderen Datenbanksystem nichts anderes.
Persönlich fällt mir beim Name DocumentStore und den genannten Vorteilen ziemlich schnell die Indizierung von Dateien auf dem Desktop ein, da hier immer wieder andere Daten anfallen. Jede Datei hat zwar eine Erstell- und Bearbeitungszeit sowie einen Dateinamen, allerdings haben Textdateien einen lesbaren Inhalt, OpenOffice-Dateien einen XML-strukturierten-Inhalt mit Meta-Informationen, Bild-Dateien verschiedene EXIF-Daten zu Aufnahmeeinstellungen (und selbst hier immer wieder unterschiedliche) und so weiter.
Mit CouchDB dürfte dies kein Problem sein, da die maximale Dokumentengröße hier auf 4GB erhöht wurde, bei MongoDB beträgt sie 8MB. Bei sehr großen Textdateien (mit dem Binärcode von Videos kann man in einer Datenbank sowieso nichts anfangen) ist die Limitierung von 8MB zu klein, jedoch ist es möglicherweise auch bei dokumentenbasierten Datenbanken praktischer, bei großen Dateien nur noch den Pfad zur Originaldatei anzugeben, wenn man nur an den Meta-Informationen interessiert ist.
I do not maintain a comments section. If you have any questions or comments regarding my posts, please do not hesitate to send me an e-mail to blog@stefan-koch.name.