Umdenken bei NOSQL: Datenkonsistenz in RavenDB
Bereits im Einstiegspost wurden auf die Vor- aber auch auf die Nachteile von einer NoSQL Datenbank (in meinem Fall RavenDB) eingegangen. Die größte gedankliche Hürde ist das die Datenkonsistenz nicht so einfach sicherzustellen ist, wie in einer klassischen Datenbank.
Ich sprech jetzt mal von RavenDB, weil ich nicht wirklich weiß ob dies bei anderen NoSQL Datenbanken ähnlich ist. Falls ich bei dem geschriebenen auf dem Holzweg bin, sagt mir einfach Bescheid ![]()
Wo liegt das Problem?
In einer klassischen Datenbank kann ich Verknüpfungen zwischen Tabelleneinträgen herstellen. Der heilige Gral der Schulweisheit ist eine Datenbank in der 3. Normalform. Beispiel: Ein Kunde macht Bestellungen und kauft dabei ein Produkt.
In Tabellenform gedacht hätten wir nun eine Kunden-Tabelle, eine Produkt-Tabelle und eine Bestellung-Tabelle mit der Referenz zum Kunden und zu einem Produkt. Das Beispiel kann man noch beliebig aufblähen und kennt wahrscheinlich jeder ![]()
Es gibt kein Schema…
In RavenDB ist es etwas anders. Dort gibt es keine direkten Verknüpfungen zwischen Dokumenten. Jedes Dokument steht für sich alleine. Siehe auch dieses Bild:
![]()
In der RavenDB Doku ist dieser Abschnitt dazu besonders interessant und für Einsteiger empfehlenswert. Natürlich gibt es auch hier “Verbindungen”, allerdings sind diese hier deutlich weicher als in einer klassischen Datenbank. Unter “blogs/9431” gibt es die Verbindung zu dem “users/ayende” Dokument, jedoch lädt RavenDB nicht automatisch irgendwelche Daten nach. Dieses Verhalten kennt man von diversen ORMs, welche über User.Orders.Products.First() witzige SQL Statements erzeugen und die Datenbank unter Last setzt ![]()
Hat man dadurch nicht Daten doppelt?
Hier fängt meine “gedankliche Blockade” an – jedoch ist die doppelte Datenhaltung in diesem Fall beabsichtig: Daten sollen doppelt gehalten werden. Wenn wir die Tags und Kategorien an einem Post brauchen um diesen im Frontend darzustellen, dann hängen wir diese Daten da auch mit ran.
(Natürlich kann man solche Sachen auch mit klassischen Datenbanken machen und Daten doppelt vorhalten oder “cachen” – allerdings macht man dies meist aus Performancegründen und nicht weil es so schön elegant ist
)
Updateszenarien
Schwierig wird es natürlich nun wenn ich eine Kategorie komplett umbenennen will. In diesem Fall muss man wohl über alle Posts gehen und schauen ob die Kategorie noch dran ist. ![]()
An dieser Stelle muss man natürlich entscheiden: Macht es Sinn die Kategorien überall umzubenennen? Wenn wir das Kunde/Bestellsystem Beispiel von oben nehmen: Der Kunde kauft zum Zeitpunkt A ein Produkt namens “Nimbus 2000”. Nun ändere ich zum Zeitpunkt B aber den Namen des Produkt in “Nimbus 2010”. Jetzt sieht es allerdings in der Datenbank so aus als hätte der Kunde den “Nimbus 2010” gekauft. Interessanter wird es noch, wenn ich das Produkt löschen möchte…
Zum Teil macht es auch extrem Sinn die Daten “doppelt” zu halten. Nicht nur von der Performance, sondern auch vom fachlichen.
Was passiert wenn ich ein Feld hinzufüge oder entferne? Wie behalte ich da die Kontrolle?
Diese Frage kam vorhin bei Golo Roden auf. Was passiert, wenn ich nun zu einem Produkt noch zusätzliche Daten abspeichern möchte? Oder ich möchte ein bestimmtes Merkmal nicht mehr speichern? Wie geht RavenDB damit um?
Um es mal einfach zu sagen: RavenDB ist es egal – es serialisiert am Ende nur Objekte als JSON. Alle Daten eines Dokumentes sind als JSON abgespeichert. Da gibt es kein Schema. Wenn ein Property hinzukommt, dann wird es beim Serialisieren berücksichtig. Wenn ich auf “leere” oder “nicht vorhandene” Felder zugreifen möchte bekomme ich in RavenDB einfach ein leeres Objekt zurück. In diesem Fall muss ich mich in der Applikation darum kümmern.
Wenn ein Property entfernt wird, dann wird es im Prinzip auch ignoriert und beim nächsten Speichern wird es auch entfernt – weil das Property nicht mehr vorhanden ist und die alten Daten überschrieben werden.
Schwieriger wird es wenn Properties umbenannt werden, aber auch gibt es ein Extension Point.
Wer vor diesen Problemen steht, sollte nochmal ein Blick auf diesen Blogpost von Ayende werfen:
RavenDB Migrations: Rolling Updates
Auch sehr interessant der Blogpost zu Migrations.
Aller Anfang ist schwer…
Definitiv ist bei NoSQL Datenbanken ein Umdenken erforderlich. So ganz klar und geheuer ist es mir ja noch nicht ganz, aber ich schau immer mal bei der RavenDB Demoapp Raccoon Blog wie der Schöpfer es da gemacht hat ![]()







Thomas Freudenberg
26. July 2011
Tja, ein Umdenken ist hier wirklich erforderlich. Mit NoSQL muss man sich andere Gedanken machen bzgl Denormalisierung bzw der Frage, was sind meine Root-Aggregates? Ich spiele mit RavenDB seit über einem Jahr rum, und mir kommt NoSQL mittlerweile wesentlich “natürlicher” vor als ein RDBMS.
> Unter “blogs/9431” gibt es die Verbindung zu dem “users/ayende” Dokument, jedoch lädt RavenDB nicht automatisch irgendwelche Daten nach
Auch das hat Ayende sehr elegant gelöst: Wenn ein Dokument A eine Referenz auf ein anderes Dokument B hält, kann ich schon beim Laden von A anweisen, dass auch B geladen werden soll, siehe http://ravendb.net/faq/includes
Thomas Freudenberg
26. July 2011
Noch ein weiterer Punkt zum Umdenken: Die Denormalisierung macht vor allem Sinn, wenn Du selten Daten änderst.
Um bei Deinem Beispiel zu bleiben: Der Namen des Artikels “Nimbus 2000″ wird sich wohl eher selten ändern, d.h. der Fall, dass Du ein vorhandenes Dokument aktualisieren mußt, ist wesentlich seltener, als dass Du eine Bestellung laden musst. Gegenüber einem klassischen, normalisierten RDBMS sparst Du Dir also die leidlichen JOINs, da Du alle Informationen bereits in einem Dokument beisammen hast. Die Updates dagegen treten sehr selten auf.
Kurzum: wenn die Zahl der Reads die Writes stark übersteigt, ist NoSQL sehr interessant.
Tom Mayer
18. February 2012
Dein Artikel Robert drückt die Gehirnwäsche aus, die jeder mitmacht, der mit SQL zu NoSQL kommt. Es ist so, als wenn du jahrelang Single warst und dann anfängst dein Leben mit einer Mietze zu teilen.. es erfordert radikales Umdenken ;o)
Die von dir diskutierten Updates auf Dokumente sind eigentlich gar kein Problem. Hier kann man elegante Lösungen finden. Stell dir vor, du änderst nachträglich den Tag „C#“ auf „I love C#“ deiner Blog Dokumente.. nehmen wir mal an du hast viele, sehr viele Blogs bereits geschrieben. Mehr als 1.000.0000. Dann ist ein Update per Pull-In sehr „teuer“ und du müsstet jedes Dokument „anfassen“. Geht aber auch anders. Stell dir vor, du führst die Änderungen an den Tags in einer separaten Datenbank mit und jedes Mal wenn ein User ein Blog öffnet, gleicht dein System vor der Anzeige alle ausstehenden Änderungen an dem Blogdokument ab, sofern notwendig… das ist cool, weil sehr günstig; erfordert wenig Operations und ist schnell. Das Prinzip dahinter sind nicht neu – „lazy write“.
Das eigentliche Problem sind gar nicht so sehr die Updates… Nehmen wir mal an du hast sehr aktive User und binnen 14 Tagen sind 20% alle Blogeinträge durch das „Update-per-Request“ (oder lazy write) Verfahren bereits aktuell, also auf „I love C#“ aktualisiert. Was aber, wenn du nach 14 Tagen anfängst deine Datenbank zu fragen: „Gib mir alle Blogs mit dem I love C# Tag“. Korrekt, jetzt wird heikel, weil 80% der Dokumente noch C# als Tag gespeichert haben… was nun?
Auf jeden Fall nicht das Ende der Welt.. auch hier hast du wieder viele Möglichkeiten. Stell dir vor du führst nicht nur Änderungen an Dokumenten separat mit, sondern auch Änderungen an deren Index. Also du weißt, wenn ich die Anfrage „gib mir alle Blogs mit Tag I love C#“ stelle, dann muss ich auch die Anfrage „gib mir alle Blogs mit Tag C#“ stellen, andernfalls ist das Ergebnis inkonsistent.
Ich glaube die größte Herausforderung für uns Klassiker aus der SQL Welt ist, dass man sich von seinem 100% picke feinen Datenbankmodellen verabschieden muss. Dokumentendatenbank haben ehr etwas vom World Wide Web.. ändert sich heute eine Webpage, dann wird google erst in ein paar Tagen darauf reagieren… ändert jemand heute das Tag in seinem Blog (aus dem Beispiel), dann kann es sein dass seine Blogdokumente erst Jahre später alle aktuell sind (rein theoretisch). Aber wenn der Nachverfolgungs- oder Änderungsmechanismus stimmt ist es nicht weiter schlimm. Und stell dir mal vor, es gibt nach zwei Jahren immer noch ein Blogdokument, dass über den alten Tagnamen C# verfügt…. zum Glück haben wir damals kein Update auf dieses Blogdokument gemacht, weil es sowieso niemand gelesen hat… cool wir haben damit Rechenzeit, Strom und Energie gespart. Wow. Wie man sieht ist NoSQL sogar umweltfreundlich;)
Oder dir gefällt die Idee nicht, die Indizes über eine Art Nachverfolgungsalgorithmik zu lösen? Okay, dann kannst du die Indexierung der „Raven“ Dokumente z.B. über eine In-Memory Datenbank wie Redis selbst immer aktuell mitführen. Das hier sind natürlich nur Ansätze und müssen je nach Anforderung durchdacht werden… klar!
Und dann hast du ja noch die „includes“, wie von meinem Vorschreiber und Namensvetter bereits erwähnt. Für dessen Hinweis ich dankbar bin… man lernt nie aus. Allerdings denk ich mir jetzt, sollte man es damit nicht übertreiben, um den Vorteil einer Dokumentenorientierten Datenbank nicht zu verschenken.
Robert Mühsig
6. March 2012
Vielen Dank für dein Kommentar – wie du bereits geschrieben hast: Die “Inkonsistenten” muss man selber managen oder es wird einfach etwas “unsauberer” (ob das gut oder schlecht ist, sei dahingestellt
).
Wo man früher immer auf das Konzept ACID gepocht hatte, kommt man immer mehr Richtung BASE (http://en.wikipedia.org/wiki/Eventual_consistency).
Auf alle Fälle: Spannend.
Daniel Lang
9. March 2012
Die Idee mit dem Index funktioniert so leider nicht, weil lucene keine interceipts kann, d.h. man kann also nicht einfach eine query formulieren mit “c#” oder “i love c#” und das selbe ergebnis erwarten, denn es ändert sich dabei die relevanz der dokumente (lucene-dokumente) -> sprich: die reihenfolge ist anders. Je nach Appkikation kann das egal sein, oder eben auch nicht. Bei ‘seriösen’ Businessanwendungen ist es meistens nicht egal, insofern bleiben dann die rolling updates nur eine nette Idee.
RavenDB ist cool, ja. Nach mehreren realen Projekten und auch ein bisschen Erfahrung im Team, kann ich allen aber nur dringend raten sich vorher genau zu informieren, auf was man sich da einlässt. Der Performance-Benefit kommt nicht umsonst und der Mythos mit der verkürzten Entwicklungszeit verpufft auch ganz schnell, wenn man das erste mal versucht eine Summe über Rechnungsdokumente zu machen, sodass man sich den Gesamtumsatz ausrechnen kann.
Tom Mayer
12. March 2012
double post!? grrr… sorry… its late:o)
Hi Daniel,
hab deinen Post gelesen und gib dir kurz ein Update. Ich arbeite seit 3 Monaten mit im Research, um für ein Projekt eine passende coSQL (wie MS die NoSql Welt neuerdings nennt) Datenbank für unsere Ansprüche zu evaluieren.
Zunächst kann ich deine Anmerkung in Bezug auf Aggregation mit RavenDB teilen; nach vielen Prototypen und Tests ist RavenDB hierfür gänzlich unausgereift – oder besser- schlicht weg nicht dafür designt worden. In deinem Beispiel für jeden Umsatzreport einen Map/Reduce über den Cluster zu schicken ist nicht nur aufwendiger als ein netter SQL Statement, sondern auch wesentlich „teuer“. Das erklärt vermutlich auch die langanhaltende Blogreihe von ayende „Whats wrong here?“ bei der das Northwind Beispiel für RavenDB diskutiert wird. Wir sind nach unserer RavenDB Begeisterung dann zu CouchDB gekommen und fanden dort (neben einem ACID Model, was für Geschäftsanwendung durchaus interessant sein kann) die Views, die das Problem der Aggregation wesentlich vereinfachen; diese werden auch über den Cluster repliziert und sind aufgrund Ihrer Persistenz wesentlich performater als jedesmals mittels M/R die Daten neu durchwühlen zulassen.
Auch cool und da sind wir derzeit dran ist Couchbase (eine Komposition aus Membase Key/Value Storage, der seine Daten mittels CouchDB persistiert). Dort gibt es – und das ist wirklich abgefahren – nun UNQL, eine SQL ähnliche Sprache für unstrukturierte Datenbanken. Da dieser Standard zusammen mit Microsoft entwickelt wurde und wird (allerdings nicht nur mit Couchebase alleine) und ich vor kurzem diesbezüglicher einer interessanten Key Note beiwohnen durfte, darf man ruhig die Prognose wagen, das Unql zu dem SQL Ersatz für coSQL und defacto-Standard für co(oder no)SQL Datenbanken „werden könnte“ (weiß es nicht sicher, aber denke Unql ist auch in CouchDB impl.)
Diesbezüglich absolut deiner Meinung, wer sich blindlinks auf einen DocumentStore stürzt, nur weil hier die Latenzzeit atemraubend sein kann oder LinQ unterstützt wird, der kann böse Überraschungen – hoffentlich – überleben; mich hats da genauso erwischt (du hättest mich noch vor zwei Wochen über RavenDB sprechen hören sollen – an dieser Stelle – es ist ein ausgezeichnetes Produkt… doch prüfe wer sich ewig binde).
Ein letztes Wort –ganz kurz, will Roberts Blog nicht volltexten – zu den Rolling Updates. In einem Posts von dir Daniel „How to handle relations in RavenDB“ ist kurz das Konzept „Denormalize your references (partly)“ besprochen; hier kann ein Rolling Update-Konzept tatsächlich ein Implementierungsweg sein (das Konzept ist in ähnlicher Form in der REST Architektur für das Caching implementiert; If-None-Match-Header oder If-Modified-Since-Header). Wenn mich jemand fragen würde ob Rolling Updates für eine Bloganwendung okay sind, dann denke ich ja.
Happy coSQL und beste Grüße,Tom
Daniel Lang
13. March 2012
Tom, danke für deine wertvollen Tipps und Anregungen. Dein Kommentar war wirklich interessant zu lesen!
Wegen dem Namen – habe dieses coSQL jetzt das erste Mal gehört. Wäre interessant wenn sich das durchsetzen würde, kann mir das aber überhaupt nicht vorstellen. Abwarten und Tee trinken…
Robert Mühsig
13. March 2012
@Tom/Daniel: Lagen die Probleme eher bei RavenDB oder generell beim Konzept der DocomentDBs? Ich habe bislang nur ein kleines Hobby-Projekt und bin noch in der “alles-ist-gut-Phase”. coSQL muss ich mir auf alle Fälle merken
Daniel Lang
13. March 2012
Keine Probleme. Nach wie vor würde ich eine überwiegende Anzahl neuer Projekte mit RavenDB beginnen. Aber ein bisschen Ernüchterung schon, dass Dokumentendatenbanken nicht _der_ Universalhammer sind und nach der überschwinglichen Euphorie auch Ecken und Kanten zum Vorschein kommen, die einem gute Gründe liefern eben nicht eine Dokumentendatenbank zu wählen und stattdessen bei einer relationalen Datenbank (PostgreSQL ist eine echte Alternative, btw.) zu bleiben.