Daten für Web-Applikationen verwalten mit der NoSQL-Datenbank CouchDB

Aus LinuxUser 08/2012

Daten für Web-Applikationen verwalten mit der NoSQL-Datenbank CouchDB

© Malalena, sxc.hu

Komfortables Sofa

Mit CouchDB und wenigen Javascript-Kenntnissen programmieren Sie im Nu eine Web-Applikation inklusive Datenbank. Ganz ohne Umdenken greift das neue Konzept aber nicht.

Eine Datenbank aufzusetzen, übersteigt die Möglichkeiten der meisten Heimanwender. Bietet der gemietete Webspace zusätzliche keine vorkonfigurierte Datenbank an, war das früher der Todesstoß für einigermaßen komplexe Anwendungen im Web. CouchDB [1] eröffnet einen einfacheren Weg, um nicht nur Daten, sondern komplette Programme zu speichern und auszuliefern.

Die Wurzeln des Projektes reichen zurück bis in die Zeit kurz nach der Jahrtausendwende: Nach seinem Studium arbeitete Damien Katz lange Zeit bei IBM an Lotus Notes. 2002 verließ er seinen Arbeitgeber und begann eine eigene Open-Source-Datenbank zu entwickeln. Sie sollte wie die von Notes arbeiten, aber wesentlich mehr Funktionen bieten. Als Programmiersprache wählte er Erlang [2] – mit dem sich die parallelen Zugriffe auf die gespeicherten Daten besonders einfach umsetzen ließen. Die erste Version der CouchDB getauften Datenbank erschien schließlich 2005.

Da Damien Katz in dieser Phase kaum Geld mit der Software verdiente, arbeitet er kurzzeitig für MySQL, das Unternehmen hinter der gleichnamigen Datenbank. Schließlich landete er wieder bei IBM. Dort durfte er weiterhin an CouchDB schrauben, musste aber die Rechte an der Datenbank an die Apache Software Foundation übergeben. Seit Ende 2008 ist das Programm ein vollwertiges Apache-Projekt und steht somit komplett unter der freien Apache-Lizenz.

2010 erreichte CouchDB die Versionsnummer 1.0. Bis dahin lief sie jedoch schon in zahlreichen Projekten und erfreute sich einer recht große Anhängerschaft. Mittlerweile gehört die Applikation zu den bekanntesten Datenbanken, die auf SQL als Abfragesprache verzichten. Solche Datenbanksysteme firmieren derzeit unter dem zwar modischen, aber leicht missverständlichen Begriff NoSQL.

Installation

CouchDB liegt einigen wenigen Distributionen bereits bei – allerdings mitunter in ziemlich alten Versionen. Das Repository von Ubuntu 12.04 bietet beispielsweise die Version 1.0.1 an, zum Redaktionsschluss aktuell war aber bereits die Version 1.2.0. Folglich kommen Sie meist nicht umhin, selbst den CouchDB-Quellcode zu übersetzen.

Dazu installieren Sie zunächst über den Paketmanager Erlang OTP (in der Version R12B5 oder höher), Make, GCC für C++, ICU, OpenSSL, Mozilla SpiderMonkey (mindestens in Version 1.7), Libcurl und Help2man. Unter Ubuntu 12.04 und Debian geht das am schnellsten über den folgenden Befehl in einem Terminal:

$ sudo apt-get install build-essential erlang erlang-eunit libicu-dev libmozjs-dev libcurl4-openssl-dev

Anschließend laden Sie von der CouchDB-Homepage oder der Heft-DVD das Archiv herunter und entpacken Sie es im Home-Verzeichnis. Dort übersetzen und installieren Sie CouchDB mit den folgenden drei Befehlen:

$ ./configure
$ make
$ sudo make install

Anschließend starten Sie CouchDB via sudo couchdb. Steuern Sie jetzt in einem Browser die Adresse http://127.0.0.1:5984/ an, meldet sich CouchDB mit {"couchdb":"Welcome","version":"1.2.0"}.

Schriftstücke

CouchDB speichert immer komplette Dokumente – wenn auch nicht Dateien im Sinne eines LibreOffice-Dokuments, sondern Textdateien im Austauschformat JSON [3]. Listing 1 zeigt ein kleines Beispiel für ein solches Dokument. In den geschweiften Klammern stehen alle zu speichernden Informationen. Um diese später einfacher auffinden und unterscheiden zu können, erhält jeder Wert zunächst einen eindeutigen Namen, den sogenannten Key.

Listing 1

{
"name": "Max Mustermann",
"strasse": "Musweg 4",
"ort": "Beidorf",
"plz": 74214,
"telefonnummern": [ "0123/4546780", "9876-543201" ]
}

Listing 1 führt beispielsweise die Ortsangabe Beidorf unter dem Key ort. Der Key selbst steht immer in Anführungszeichen. Ihm folgen ein Doppelpunkt und dann die eigentliche Information. Sofern es sich dabei um einen Text handelt, steht dieser ebenfalls in Anführungszeichen, bei Zahlen dürfen sie diese weglassen.

Die zu speichernden Daten trennen jeweils Kommas – in Listing 1 am Ende einer jeden Zeile. Aufzählungen beziehungsweise Listen stehen zwischen eckigen Klammern, wobei ebenfalls wieder Kommata die einzelnen Werte trennen. Welche Daten ein Dokument enthält, dürfen Sie frei festlegen. Als Werte erlaubt die Software übrigens wiederum JSON-Strukturen, wie das Beispiel aus Listing 2 zeigt.

Listing 2

{
"name": "Max Mustermann"
"bestellung": 1
"produkte": [
{"bestellung": "T-Shirt"
"anzahl": 1
},
{
"bestellung": "Schuhe"
"anzahl": 2
} ]
}

Anfragen und Anweisungen nimmt CouchDB über das HTTP-Protokoll entgegen, das Browser zum Abrufen von Webseiten verwenden. Vereinfacht gesagt: Sie rufen spezielle URLs auf, die CouchDB dann wiederum als Anweisungen interpretiert. Das Prinzip ist unter dem Namen RESTful [4] bekannt.

Am einfachsten setzen Sie solche Anfragen über das Hilfsprogramm cURL [5] ab, das sich in den Repositories aller gängigen Distributionen findet. Eine neue Datenbank adressbuch erzeugen Sie über den folgenden Aufruf:

$ curl -X PUT http://127.0.0.1:5984/adressbuch

Das Kommando PUT bezeichnet die entsprechende HTTP-Methode. Sie weist CouchDB an, etwas zu erstellen oder zu verändern. Hinter der Adresse 127.0.0.1 verbirgt sich der eigene Rechner, auf dem CouchDB standardmäßig am TCP-Port 5984 lauscht. CouchDB liefert also grundsätzlich auch alle Ausgaben in der JSON-Notation zurück. Die Rückmeldung auf diesen Befehl sollte {"ok":true} lauten.

Nummern-Girl

Die erstellte Datenbank können Sie jetzt mit Daten füllen – wie etwa denen aus Listing 1. Wie alle in CouchDB gespeicherten Dokumente benötigt auch dieses allerdings noch einen eindeutigen Namen – CouchDB speichert ihn später im Dokument unter dem Key _id.

Sie dürfen sich für jedes Dokument selbst einen Namen ausdenken, solange er keine Sonderzeichen enthält und nicht mit einem Unterstrich beginnt – solche Namen reserviert CouchDB für interne Zwecke. Wichtig ist, dass Sie einen eindeutigen Namen wählen. Um das sicherzustellen, generieren Sie diesem am einfachsten über CouchDB (Abbildung 1):

$ curl -X GET http://127.0.0.1:5984/_uuids
Abbildung 1: Den Namen des Dokuments hängen Sie einfach an die URL an. In diesem Fall dient als Name die zuvor von CouchDB erzeugte UUID.

Abbildung 1: Den Namen des Dokuments hängen Sie einfach an die URL an. In diesem Fall dient als Name die zuvor von CouchDB erzeugte UUID.

Als Ergebnis erhalten Sie eine lange, kryptische Zeichenkette: einen sogenannten Universally Unique Identifier, kurz UUID [6]. Unter Verwendung des Namens können Sie jetzt das Dokument in die Datenbank stecken, den dazu nötigen cURL-Befehl zeigt Listing 3.

Listing 3

$ curl -X PUT http://127.0.0.1:5984/adressbuch/6825dc0758d90d1ec96b57dbdc000c38 -d '{"name": "Max Mustermann","strasse": "Musweg 4","ort": "Beidorf","plz": 74214,"telefonnummern": [ "0123/4546780", "9876-543201" ]}'

Durch das angehängte Dokument verwandelt der Befehl sich in einen ziemlich unhandlichen Bandwurm. Wollen oder müssen Sie Dokumente per Hand in die Datenbank stopfen, sollten Sie deshalb die Hilfe der mitgelieferten Oberfläche in Anspruch nehmen. Sie erreichen sie mit einem Browser unter der Adresse http://127.0.0.1:5984/_utils/.

Sie sehen dann die in CouchDB eingebaute Web-Applikation namens Futon (Abbildung 2). In dieser klicken Sie die entsprechende Datenbank in der Liste an, im Beispiel also das adressbuch. Die anderen Datenbanken, deren Name mit einem Unterstrich beginnt, nutzt CouchDB für interne Zwecke. Im Zweifelsfall erstellen Sie mit Create Database unkompliziert eine neue Datenbank.

Abbildung 2: CouchDB bringt ein eingebautes Frontend mit dem Namen Futon mit, über das Sie die Datenbanken etwas komfortabler verwalten.

Abbildung 2: CouchDB bringt ein eingebautes Frontend mit dem Namen Futon mit, über das Sie die Datenbanken etwas komfortabler verwalten.

In der geöffneten Datenbank legt Futon über New Document ein weiteres leeres Dokument an und vergibt dafür automatisch eine ID. Die müssen Sie nur über die entsprechende Checkbox bestätigen – oder aber den Vorschlag mit einer eigenen ID überschreiben.

Per Add Field ergänzen Sie jetzt sukzessive weitere Datenfelder. Für eine Änderung genügt ein Klick auf den entsprechenden Eintrag (Abbildung 3). Alternativ geben Sie auf dem Register Source das komplette Dokument in der JSON-Notation ein beziehungsweise bearbeiten es.

Abbildung 3: Futon ermöglicht das komfortable Anlegen und Bearbeiten eines Dokuments – hier der Adresse von <code srcset=

Max Mustermann.” width=”300″ height=”233″ /> Abbildung 3: Futon ermöglicht das komfortable Anlegen und Bearbeiten eines Dokuments – hier der Adresse von Max Mustermann.

Altlasten

Egal, ob Sie ein Dokument über die Kommandozeile oder Futon in die Datenbank schieben: CouchDB fügt dem Dokument automatisch eine Versionsnummer hinzu. Die liegt im Key _rev und setzt sich aus zwei Teilen zusammen: Am Anfang vor dem Bindestrich steht eine fortlaufende Nummer.

CouchDB zählt diese nach jeder Änderung um eins hoch. Zudem erstellt CouchDB mittels des MD5-Verfahrens eine Prüfsumme des Dokuments, die hinter dem Bindestrich steht. Die älteren Versionen des Dokumentes bleiben weiterhin in der Datenbank gespeichert. In Futon haben Sie die Möglichkeit, am unteren Rand über Previous Version durch diese zurückzublättern.

Wenn Sie ein Dokument mit cURL ändern oder löschen, müssen Sie die komplette Versionsnummer mit angeben. Der Befehl aus Listing 4 erweitert beispielsweise das vorhandene Dokument um den Eintrag alter. Sofern dieser Key noch nicht existiert, erzeugt ihn der Befehl, andernfalls ändert er seinen Wert. Die erneute Angabe der übrigen Keys ist notwendig, da CouchDB sie andernfalls löschen würde. Wie Sie in Futon oder via Terminal schnell herausfinden, hat CouchDB die Revisionsnummer entsprechend erhöht.

Listing 4

$ curl -X PUT http://127.0.0.1:5984/adressbuch/6825dc0758d90d1ec96b57dbdc000c38 -d '{"_rev": "1-b2148264bd1a386e43da2ebfe6794d41", "name": "Max Mustermann","strasse": "Musweg 4","ort": "Beidorf","plz": 74214,"telefonnummern": [ "0123/4546780", "9876-543201" ],"alter": 24}'

Wie das Beispiel zeigt, dürfen Sie einem Dokument jederzeit beliebige weitere Daten hinzufügen. Die anderen Dokumente können komplett andere Informationen enthalten – so dürfte die Adresse von Peter Schmidt auch nur seine Mobilfunknummer umfassen. Im Gegensatz zu herkömmlichen relationalen Datenbanken ist CouchDB folglich schemafrei.

Die Pflicht zur Angabe der Revisionsnummer stellt übrigens keine Schikane dar: Möchten zwei Benutzer Daten verändern, sperren andere Datenbanken den Zugriff zunächst für den zweiten Benutzer. Erst wenn der erste Benutzer alle Operationen ausgeführt hat, ist der zweite Benutzer an der Reihe. Dieses Blockieren führt unter anderem zu einer schlechteren Performance.

CouchDB setzt deshalb auf das Multiversion-Concurrency-Control-Verfahren (MVCC). Dabei dürfen alle Benutzer immer alle Daten aus der Datenbank lesen. CouchDB liefert dann jeweils die gerade aktuelle Version des Dokuments zurück. Aktualisiert nun ein Benutzer ein Dokument, prüft CouchDB, ob eventuell schon ein Anderer schneller war. Dazu vergleicht CouchDB die mitgegebene Versionsnummer mit der aktuellen in der Datenbank. Weichen die beiden Zahlen ab, meldet CouchDB einen Konflikt, den Sie auflösen müssen.

Abbilden und Reduzieren

Komplexe Abfragen erfolgen in CouchDB auf einem etwas ungewöhnlichen Weg. Jede Abfrage besteht aus zwei Phasen: Die erste wählt zunächst einen Teil der Dokumente aus, den die zweite Phase dann weiterverarbeitet. Sucht zum Beispiel die erste Phase alle Adressen mit dem Namensteil Schmidt heraus, wäre es möglich, die Anzahl dieser Adressen zu zählen. Die erste Phase heißt “Map”, die zweite “Reduce”.

In der ersten Phase durchläuft CouchDB alle Dokumente in einer Datenbank. Dabei übergibt die Software jedes einzelne Dokument einer Javascript-Funktion. Die wiederum entscheidet, ob das Dokument für das weitere Verarbeiten relevant ist. Listing 5 zeigt in den ersten drei Zeilen eine einfache Map-Funktion.

Listing 5

function(doc) {
  if (doc.name == 'Schmidt') emit(doc.name, 1)
}
function(keys, values, rereduce) {
  return sum(values)
}

Diese prüft, ob das von CouchDB übergebene Dokument einen Key name mit dem Wert Schmidt enthält. Falls ja,, gibt sie nicht etwa die komplette Adresse zurück, sondern via emit() lediglich einen Schlüssel namens Schmidt mit dem Wert 1. Als Ergebnis der ersten Phase erhalten Sie dann eine Liste mit Zwischenergebnissen der Form:

"Schmidt": 1
"Schmidt": 1
"Schmidt": 1
...

Diese Liste stopft CouchDB jetzt in eine zweite Funktion (Listing 5, Zeile 5 bis 7). Diese zählt jetzt einfach alle gefundenen Zwischenergebnisse.

Das Ergebnis ist die Anzahl aller Schmidts im Adressbuch. Für Umsteiger von SQL mag das ziemlich kompliziert wirken, zumal zwingend Javascript-Kenntnisse erforderlich sind. Umgekehrt ermöglicht CouchDB gerade dank dieses Umstands besonders komplexe Abfragen und Berechnungen.

Die Reduce-Funktion dürfen Sie in CouchDB weglassen – etwa, wenn Sie einfach nur an den Adressen aller Schmidts interessiert sind. Das Ausführen im Hintergrund übernimmt die Software Spidermonkey, die ebenfalls in Firefox den Javascript-Code interpretiert. Über sogenannte View-Server binden Sie aber bei Bedarf weitere Sprachen an – derzeit unter anderem PHP, Perl und Python.

Mit den zwei Funktionen ist es aber noch nicht getan: Beide müssen Sie in einer sogenannten View zusammenfassen und diese wiederum in einem speziellen Design-Dokument ablegen. Wie das mit den beiden Funktionen aussieht, zeigt Listing 6.

Listing 6

{
  "_id": "_design/adressbuch",
  "views": {
    "AnzahlSchmidt": {
      "map": "function(doc) { \
        if (doc.name == 'Schmidt') emit(doc.name, 1) \
      }",
      "reduce": "function(keys, values, rereduce) { \
        return sum(values) \
      }"
    }
  }
}

Beide Funktionen lagern unter den Keys map und reduce – das gibt CouchDB so vor. Dieses Duo wiederum fasst der Key AnzahlSchmidt zusammen. Diesen Namen dürfen Sie frei wählen. Die Ansicht AnzahlSchmidt steckt wiederum im Key views (auch das gibt die Software so vor). Das Dokument erhält schließlich noch einen Namen, der mit _design/ beginnt. Dies kennzeichnet das Dokument als Design-Dokument.

Listing 6 legen Sie als neues Dokument in der Datenbank ab, im Beispiel also im adressbuch. Anschließend starten Sie die Abfrage über den Befehl:

$ curl http://127.0.0.1:5984/adressbuch/_design/adressbuch/_view/AnzahlSchmidt

Sie brauchen also nur eine simple URL aufzurufen. Wie in Abbildung 4 erscheint das Ergebnis standardmäßig wieder im JSON-Format.

Abbildung 4: Hier liegen zwei Adressen mit dem Namen <code srcset=

Schmidt in der Datenbank. Wie rows andeutet, darf eine Anfrage durchaus mehrere Ergebnisse liefern.” width=”300″ height=”93″ /> Abbildung 4: Hier liegen zwei Adressen mit dem Namen Schmidt in der Datenbank. Wie rows andeutet, darf eine Anfrage durchaus mehrere Ergebnisse liefern.

Schminktisch

Sowohl die gespeicherten Dokumente als auch die Ergebnisse von Abfragen lassen sich umformatieren und beispielsweise als HTML-Seite ausgeben. Dies übernimmt wieder eine Javascript-Funktion. Gibt diese nur den Inhalt eines einzigen Dokuments aus, handelt es sich um eine sogenannte Show-Funktion.

Einen einfachen Vertreter dieser Gattung zeigt Listing 7 ab der Zeile 5. Sie presst lediglich Namen, Straße und Ort des übergebenen Dokuments in eine einfache HTML-Seite und liefert diese zurück. Über die Fallunterscheidung prüfen Sie, ob es überhaupt ein Dokument gibt (beziehungsweise die Funktion eines erhalten hat).

Listing 7

{
"_id": "_design/adressbuch",
"views": {
  ...},
  "shows": {
    "htmladr": "function(doc, req) {
      if(doc) {
        return '<!DOCTYPE html><html><head><title>Ein Dokument</title></head><body><p>Name: '+doc.name+'</p> <p>Straße: '+doc.strasse+'</p> <p>Ort: '+doc.plz+' '+doc.ort+'</p></body></html>';
      } else {
        return '<p>Es gibt kein Dokument mit der ID '+req.id+'</p>';
      }
    }"
  }
}

Wie Listing 7 zeigt, müssen Sie die Funktion unter einem frei wählbaren Schlüsselwort (im Beispiel htmladr) im Design-Dokument unterhalb des Keys shows speichern. Hängen Sie im Browser an die URL noch die ID eines Dokuments an, erhalten Sie als Ergebnis die Webseite (Abbildung 5).

Abbildung 5: Die Show-Funktion presst hier drei Informationen aus dem Dokument mit der ID <code srcset=

6825dc0758d90d1ec96b57dbdc000c38 in eine HTML-Seite.” width=”300″ height=”82″ /> Abbildung 5: Die Show-Funktion presst hier drei Informationen aus dem Dokument mit der ID 6825dc0758d90d1ec96b57dbdc000c38 in eine HTML-Seite.

Anfragen wie in Abbildung 4 produzieren zwar eine Liste von Ergebnissen, intern handelt es sich dabei aber nur um einen einzelnen Wert. Über die List-Funktion bereitet CouchDB diesen auf. Die einfache List-Funktion aus Listing 8 gibt die Anzahl der Schmidts in der Datenbank als HTML-Dokument aus, das Ergebnis erhalten Sie über die entsprechende URL (Abbildung 6).

Listing 8

{
  "_id": "_design/adressbuch",
  "views": {
    ...
  },
  "lists": {
    "htmlschmidt": "function(head, req) {
      start({'headers': {'content-type': 'text/html'}});
      send('<!DOCTYPE html><html><head><title>Anzahl Schmidts</title></head><body>');
      var row = getRow();
      send('<p>Im Adressbuch sind: '+row.value+' Personen, die Schmidt heißen.</p></body></html>');
    }"
  }
}

Abbildung 6: Die HTML-Seite präsentiert das Ergebnis der Abfrage.

Abbildung 6: Die HTML-Seite präsentiert das Ergebnis der Abfrage.

Wenn Sie die Webseiten jetzt noch ein wenig mit CSS und Javascript garnieren, haben Sie eine vollständige Web-Anwendung – eine CouchApp. Sie brauchen dazu noch nicht einmal einen Webserver. Das eingebaute Futon arbeitet übrigens nach dem exakt gleichen Prinzip.

Gefeuerte Replikanten

Bei Bedarf transferieren Sie eine Datenbank an andere laufende CouchDB-Instanz (“replizieren”). Die beteiligten Kollegen halten den Datenbestand dabei immer automatisch auf dem aktuellen Stand. CouchDB eignet sich somit hervorragend für verteilte (Web-)Anwendungen. Aus diesem Grund nutzte Canonical die Datenbank eine Zeit lang für seinen Dienst Ubuntu One. Dort sorgte CouchDB unter anderem für den Abgleich von Kontakten und Lesezeichen. Nach einer Weile stellte sich jedoch heraus, dass CouchDB den mehreren Millionen Benutzern nicht gewachsen war, sodass sich Canonical Ende 2011 dazu entschloss, die Software gegen eine Eigenentwicklung auszutauschen. Dabei heben die CouchDB-Entwickler gerade die gute Skalierbarkeit ihrer Datenbank gern hervor.

Fazit

Neben den gezeigten Funktionen bietet CouchDB noch viele weitere, darunter natürlich das obligatorische Verwalten der Rechte. Die Datenbank erfordert jedoch ein radikales Umdenken. Insbesondere Umsteiger von SQL dürften sich nur langsam an das Map-Reduce-Konzept gewöhnen.

Da es keine direkten Verknüpfungen mehr zwischen den Datensätzen gibt, ist es zudem in dokumentenorientierten Datenbanken durchaus üblich, Dokumente mehrfach zu speichern. Bei einer Bestellung könnte man beispielsweise die einzelnen Produkte noch einmal vollständig direkt im Datensatz zur Bestellung ablegen. Dadurch liegt jedes Produkt mehrfach in der Datenbank. Zusammen mit den älteren Versionen der Dokumente bläht sich der Bestand dann stetig auf.

CouchDB eignet sich folglich nicht für alle Einsatzgebiete gleichermaßen. Ein Haupteinsatzfeld stellen zweifelsohne kompakte Web-Anwendungen dar, bei denen die Datenbank sogar einen kompletten Webserver einspart.

Damien Katz stieg übrigens Anfang 2012 aus seinem Projekt aus und gründete Couchbase [7]. Das Unternehmen entwickelt eine Datenbank, die alle Vorteile der Konkurrenten CouchDB, Membase und Memcached vereinen möchte. 

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDF
LinuxUser 08/2012 KAUFEN
EINZELNE AUSGABE
ABONNEMENTS
TABLET & SMARTPHONE APPS
E-Mail Benachrichtigung
Benachrichtige mich zu:

Hinweis: Dieser Artikel ist älter als ein Jahr, enthaltene Informationen sind möglicherweise veraltet.

0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben