Sunday, October 12, 2014

Codefest: Azure unterstützt NoSQL DocumentDB. Ja, und was soll ich damit?

clip_image001


Genau das hat mich einer meiner Kunden unlängst gefragt, als diese Meldung von Microsoft durch alle verfügbaren „Techie“-Kanäle gespült wurde. Dies hat mir wieder gezeigt, dass man als Trainer und Berater zur Zeit die Möglichkeit hat, aber auch die Notwendigkeit besteht, in diesem – für viele Firmen – neuen Umfeld Aufklärungsarbeit zu leisten. Widmen wir uns somit den Möglichkeiten der Datenspeicherung unter Azure und wann man welches der angebotenen Szenarien verwendet.
Um das Ziel Datenspeicherung zu erreichen, haben wir aber einen relativ weiten Weg zu beschreiten. Fangen wir also einmal mit der generellen Unterscheidung an, wie Daten abgelegt werden können:
Zur Verfügung stehen uns
1. relationale Datenbanken und
2. NoSQL Datenbanken.
Zerpflücken wir einmal 1. und danach 2., dann werden wir sehen, dass Microsoft hier wieder einmal mit Azure sehr innovativ unterwegs ist und uns schon heute Optionen für morgen anbietet!

 

Relationale Datenbanken

Ich selbst war sechs Jahre bei Sybase und kann daher voll und ganz verstehen, dass Microsoft den Source Code von Sybase vor langer Zeit gekauft hat und daraus ein großartiges Produkt mit dem Namen Microsoft SQLServer gemacht hat. Aber was macht relationale Datenbanksysteme so großartig? Ich bin mir ziemlich sicher, dass die meisten meiner Leser gelangweilt an die Decke starren, aber da sollten wir kurz durchtauchen, um möglicherweise im Anschluss eine komplett NEUE Welt (der Daten) kennen zu lernen und zu verstehen.
Den Grundstein für relationale Datenbanksysteme (kurz RDBMS) legte ein gewisser Edgar Codd (von IBM), der im Jahre 1970 (also sogar vor meiner Zeit) zwölf Regeln aufstellte (Codd's 12 rules[Ma1] ), die genau definieren, was denn ein System können sollte, um ein RDBMS zu sein. In den frühen 80er Jahren haben sich dann die ersten Hersteller mit diesem Thema auseinandergesetzt, und – voila – die Datenbanken, so wie wir sie (in den meisten Unternehmen) kennen, haben das Licht der Welt erblickt.
Wir verwenden also einen Datenbankserver, der uns eine oder mehrere Datenbanken zur Verfügung stellt. In einer Datenbank befinden sich wiederum eine oder mehrere Tabellen in einem bestimmten Schema, mit Spalten von einem definierten Datentyp und natürlich unseren hoch geschätzten Datenzeilen – ohne die unsere Datenbank recht leer aussehen würde. Wir finden in unseren Datenbanken Primärschlüssel, Fremdschlüssel und (hoffentlich auch) Indexe. Stored Procedures, Funktionen und Views wollen wir natürlich auch nicht vergessen. Wenn Daten geschrieben/gelesen/gelöscht werden, werden dazu von unseren Applikationen Transaktionen verwendet. Dass unsere Datenbank jederzeit konsistent ist (Stichwort ACID - Atomicity, Consistency, Isolation und Durability – sichergestellt durch Transaktionsprotokoll, Locking Mechanismen und Isolation Level) setzen wir natürlich voraus. Beim Design und der Definition der Tabellen verwenden wir die Normalisierung und als Abfragesprache benutzen wir standardisiertes SQL.
Ich denke, das war es im Schnelldurchlauf - zwar recht kompakt, aber hoffentlich verständlich zusammengefasst. In Summe doch etwas Großartiges, was sich in all den Jahren auf Basis dieser 12 Regeln entwickelt hat!

 

CAP-Theorem

RDBMS sind eine feine Sache, allerdings geraten wir mit den, in den letzten Jahren immer häufiger auftretenden, großen Mengen an Daten ein klein wenig in Schwierigkeiten. Diese wollen nicht mehr so recht in einen einzigen Server hineinpassen (irgendwann hat auch das Aufrüsten mit größeren und leistungsstärkeren Festplatten, zusätzlichem Speicher und stärkeren Prozessoren ein Ende erreicht), also werden diese zu einem verteilten System. Das ist aber nicht der einzige Grund. Neben dem Ressourcenmangel gibt es aber noch weitere Gründe:
· Netzwerk-Bandbreite - die Performance auf einem einzigen Server ist abhängig davon, wie schnell eingehende Anfragen empfangen und ausgehende Resultate gesendet werden können. Wenn die Netzwerklast die Kapazität des Netzwerkes übersteigt, kommt es zu Ausfällen und im Endeffekt zu unglücklichen Anwendern.
· Regionsabhängige Server – möglicherweise wird es notwendig, Anwenderdaten in dem Land zu speichern, in dem diese anfallen (unter Azure gibt es die Auswahlmöglichkeit zwischen 15 Regionen, wo die Daten abgelegt werden können). Gründe dafür können gesetzlicher Natur, seine, compliance, oder eben wieder die Performance (Latenz der Datenzugriffe) sein. Wenn Anwender in verschiedenen Teilen der Welt verstreut sind, ist es eben vermutlich besser, mehr als einen Server zur Verfügung zu stellen.
Wenn zumindest einer dieser eben genannten Gründe zutrifft, dann werden relationale Datenbanken verteilt. Nun, das bringt uns zum sogenannten CAP-Theorem, da solche verteilten UND weiterhin leistbaren Systeme nur zwei, der drei folgenden Eigenschaften (die wir an relationalen Datenbanken so schätzen) erfüllen können:
· (C) Strong Consistency - Konsistenz: Jeder Client (gemeint sind Anwendungen, bzw. die Anwender) sieht zur selben Zeit dieselben Daten, auch bei Datenänderungen – eben auch bei Transaktionen mit 2-Phase-Commits.
· (A) High Availability - Verfügbarkeit: Alle Anfragen von Clients an das Datenbanksystem werden jederzeit und mit akzeptabler Antwortzeit beantwortet.
· (P) Partition-Tolerance - Ausfalltoleranz: Das gesamte Datenbanksystem muss weiterhin stabil laufen, auch wenn einer der eingesetzten Server ausfällt oder neue Server hinzugefügt werden. Auch der Ausfall einer Kommunikationsverbindung muss verkraftet werden können, ohne dass die Clients etwas davon bemerken. Damit Daten auch nach Ausfall eines Servers vorhanden sind, werden sie repliziert auf verschiedenen Servern gespeichert.
Relationale Datenbanksysteme sind am ehesten im Bereich CA zu finden. Konsistenz der Daten hat üblicherweise bei den relationalen Systemen oberste Priorität (mittels Transaktionen und ACID). Den Anforderungen nach Ausfalltoleranz und Verfügbarkeit wird man meist durch sehr hochwertige und sehr leistungsfähige Hardware (im wahrsten Sinne des Wortes) Rechnung tragen.
Grafisch lässt sich das so darstellen:
clip_image003
Was ist aber nun mit CP und PA ? Hier können die NoSQL-Datenbanken eine Lücke schließen – und so betreten wir das Land der NoSQL-Datenspeicherung (und wir können an dieser Stelle auch erahnen, warum).

NoSQL Datenbanken

Beginnen wir zuerst mit der Definition von NoSQL: Wir könnten uns mit dem Namen Not only SQL zufrieden geben, aber da steckt mehr dahinter, nämlich die Datenbanken der nächsten Generation, diese sind
· nicht-relational,
· verteilt,
· Open-Source, sowie
· horizontal skalierbar.
Man könnte somit geneigt sein zu sagen: So ziemlich das Gegenteil von dem, was man (zumindest als Entwickler) bisher so geschätzt hat. Aber es kommt noch dicker, es gibt da noch weitere Anregungen, was eine NoSQL-Datenbank so bietet:
· einfache Replikation,
· ein simples API
· sehr grosse Datenmengen
· und das Allerbeste hebe ich mir für den Schluss auf:
EVENTUELL konsistent und Schema-Frei
Ich hoffe, die alteingesessenen Datenbank-Hasen unter euch sitzen immer noch halbwegs gerade auf dem Sessel (dabei habt ihr möglicherweise ‚ein simples API‘ sogar überlesen, was so viel bedeutet, wie non-SQL als Abfragesprache)? Ja, das ist schon starker Tobak, klingt aber durchaus durchdacht, wenn wir uns die fünf NoSQL-Technologien ansehen, die es am Markt gibt:
· Key/Value Stores – speichern der Daten mit einem Partition Key und einem Row Key.
· Column Stores – quasi eine Erweiterung der Key/Value Stores um eine weitere Gruppierung.
· Document Stores – Speicherung von Objektstrukturen im JSON-Format.
· Graph Databases – wenn die Beziehungen zwischen den Daten im Vordergrund liegen
· Big Data Analytics – Analysieren von (unvorstellbar) großen Datenmengen.
Da ein Bild mehr als 1000 Worte sagen kann, möchte ich die NoSQL-Technologien grafisch veranschaulichen:
clip_image005
Ich möchte nun jede einzelne dieser NoSQL-Technologien beleuchten und deren Einsatzgebiete herausstreichen.

Key/Value Stores

Es gibt hier verschiedene Implementierungen, in Microsoft Azure heißt der NoSQL Key/Value Store, Azure Table Storage. Um Daten im Table Storage zu speichern, wird eine Tabelle (heißt zwar so, hat aber keine Ähnlichkeit mit einer relationalen Datenbanktabelle) mit einem 2-teiligen Schlüssel und weitere Spalten für die Daten selbst benötigt. Dieser 2-teilige Schlüssel besteht aus:
· einem Partition-Key, nach diesem wird die Tabelle partitioniert und
· dem Row Key, dieser identifiziert die Zeile eindeutig INNERHALB der Partition (fast so wie der gewohnte Primary Key, aber eben nur beinahe).
Weitere Entitäten (unsere altbekannten Spalten) können nach Belieben angelegt werden und haben einen Namen, einen Datentyp und (vermutlich) einen Wert.
Ich hatte erst vor kurzem einen interessanten Anwendungsfall für einen Key/Value Store:
Es sollte ein Sicherheitssystem mit sechs Kameras realisiert werden, diese laden die Bilder alle zwei Sekunden in die Azure Cloud (als Blob-Storage) hoch. In dem Key/Value Store wurde folgende Information gespeichert:
· Partition Key: Der Name der Applikation
· Row Key: Die ID der Kamera
· Daten: Der Name des aktuellen Azure Blobs und die Aufnahmezeit
Natürlich wäre es möglich gewesen, eine relationale Datenbank zu verwenden, aber hier war der Preis ausschlaggebend, da der Azure Table Storage mit dem Azure Blob Storage sehr günstig ist und diese Lösung nur auf wenige € im Jahr (ca. €25,--) kommt.
Azure Table Storage ist, wie andere Implementierungen auch, auf Massendaten ausgelegt. Die Partitionen einer einzelnen Tabelle können sich über mehrere Maschinen verteilen. Ein Szenario also, wo wir mit relationalen Datenbanksystemen möglicherweise in Schwierigkeiten punkto Kosten kommen, da sie mit Sicherheit höher liegen. Dieses horizontale Partitionieren der Daten wird Sharding genannt. Windows Azure Table Storage führt das Sharding im Hintergrund aus und skaliert damit automatisch bei großen Datenmengen. Aber wie werden die Probleme gelöst, die bei relationalen Systemen auftreten würden?
Jede Abfrage an einem Key/Value Storage kann nur eine Partition ansprechen, Joins sind nicht möglich. Darauf muss natürlich bereits während der Entwicklung der eigenen Applikation Rücksicht genommen werden. Wie eingangs erwähnt: Auch wir Entwickler müssen umdenken und diese neueren Konzepte erst umsetzen lernen. Daten, die also gemeinsam abgefragt werden wollen, müssen auch innerhalb einer Partition liegen – das gilt übrigens auch für Transaktionen, diese gelten nur über eine Partition.

Column Stores

Microsoft Azure bietet HBase (mit HDInsight, also Hadoop) als NoSQL Column Store Implementierung an. HBase, bzw. Column Store-NoSQL-Datenbanken erinnern stark an eine relationale Datenbank, da die Daten in Tabellen gespeichert werden, Zeilen, Schlüsseln, Spaltenfamilien, Spalten und Werte sind auch vorhanden. Also alles wie gehabt, nur ein neuer Name? Mitnichten.
In HBase ist jede Datenzeile durch einen Key definiert, diese Datenzeile beinhaltet eine, oder mehrere Spalten, welche selbst wiederum Key/Value-Pairs darstellen.
Die Spaltennamen müssen nicht vordefiniert werden, das bedeutet, dass das Schema hier ebenfalls wiederum kein fixes ist – im Gegensatz zu relationalen Datenbanken. Die Spalten in einer Datenzeile werden sortiert nach ihrem jeweiligen Namen abgelegt. Zusätzlich speichert HBase zu jeder Zeile einen Zeitstempel – zur Versionierung - mit ab. Würde ein neuer Wert in eine der Spalten eingetragen, bleibt der alte Wert weiterhin erhalten.
Anwendungsfall gefällig? Facebook-Messaging, dort wird genau diese Technologie verwendet, wobei als Zeilenschlüssel die Benutzer-IDs verwendet werden, als Spalten-Bezeichner Wörter, die in den Nachrichten der Anwender vorkommen, allerdings wird der Zeitstempel anders verwendet, da sich gesendete Nachrichten nicht mehr verändern können, es wird die Nachrichten ID abgelegt.
Die beschriebene Art der Datenspeicherung ist ideal, wenn sehr viele Spalten in einer Datenzeile vorhanden sind und spezielle Bereiche gesucht werden. Dieser Satz, umgelegt auf das FaceBook-Messaging Szenario könnte einen zum Nachdenken bringen, was denn wohl alles mit dieser Technologie möglich ist.
Als Entwickler kann man sich das als geschachtelte Hashtable oder Dictionary, mit einer Tiefe von 2 oder 3 Levels vorstellen.
Was die Performance angeht, ist HBase skalierbar, wenn also mit großen Datenmengen von mehreren Gigabyte, oder sogar Terabyte gearbeitet wird, dann ist HBase interessant. Des Weiteren kann HBase Daten schnell replizieren, sodass im Fehlerfall fehlerhafte Knoten recht problemlos wieder hergestellt werden können.

Document Stores

In einem Document Store werden sogenannte Dokumente gespeichert, darunter darf man sich aber nicht z. B Word Dokumente oder PPT-Dateien vorstellen. Was aber könnte es dann sein?
Ein Beispiel: In einer relationalen Datenbank haben wir Tabellen und Spalten. Also ein festgelegtes Schema für die abzulegenden Daten. Was aber ist, wenn die Struktur, die wir als Entwickler verwenden, nicht in dieses Schema passt? Wie oft mussten wir schon das Datenbank-Schema in die Objekt-Struktur pressen, die man in der Anwendung verwendet? Natürlich wieder genau anders herum, wenn die Applikations-Daten persistiert werden sollen. Das war und ist lästig für den Entwickler. Hier wäre es um einiges einfacher, wenn das in der Applikation verwendete Format durchgängig – in einem Document Store - zur Verfügung stehen könnte.
Ein anderes Szenario wäre ein WCF-Service, welches Daten mittels Entity-Framework einliest und diese an einen HTML/JavaScript Client übertragen soll. Das ideale (universelle) Format dafür ist JSON (JavaScript Object Notation). Die Daten, die übertragen werden sind nun ebenso ein Dokument, welches in einem Document Store abgelegt werden könnte. Also eigentlich ein Objekt oder eine Objektstruktur als Dokument.
Ein weiterer Anwendungsfall wäre ein “Einkaufswagen” in einer Shopping-Applikation. Die abgelegten Produkte haben unterschiedliche Eigenschaften – also Objekte mit unterschiedlichen Strukturen - die durch das lose Schema einer JSON-Datei problemlos vorgehalten werden können – was in einer relationalen Datenbank ungleich schwieriger wäre. So ein Einkaufswagen könnte im JSON-Format so aussehen:
clip_image006­­­
In einer DocumentDB können natürlich komplexere und längere Dokumente gespeichert werden, man stelle sich einen Blog-Eintrag und die zugehörigen Kommentare vor, ein Summenfeld mit der Anzahl der Kommentare, Erscheinungsdatum, usw.
Die Azure DocumentDB befindet sich momentan in der Preview-Phase und kann für die neugierigen Entwickler unter euch getestet werden.

Graph Databases

Was wäre, wenn die Verbindung zwischen den Daten genauso wichtig, oder sogar wichtiger wäre als erst einmal die Daten selbst? Ich durfte drei Jahre lang an einem Projekt zur Bekämpfung von Geldwäsche mitarbeiten, wo wir genau den eben angesprochenen Fall hatten. Es ist interessant, wie Überweisungen, Personen und Firmen in Beziehung stehen und zuerst einmal gar nicht die zugrunde liegenden Daten selbst. In so einem Fall sind Graph Databases der geeignete Anwendungsfall.
Unter Azure gibt es keinen Vertreter dieser Gattung, der populärste ist hier Neo4J, welches aber unter Azure verwendet werden kann.
Wie der Name schon erahnen lässt, werden die Daten als Graph abgelegt. In Neo4J werden die Daten als Knoten bezeichnet, die Verbindungen zwischen den einzelnen Knoten sind Beziehungen. Sowohl Knoten als auch Beziehungen können Eigenschaften aufweisen – die uns nun schon gut bekannten Name/Value Pairs. Neo4J arbeitet (mittlerweile wenig überraschend) ohne festem Schema, somit können die Eigenschaften jede beliebige Information beinhalten.
Eine typische Abfrage in Neo4J beinhaltet den Startpunkt (ein einzelner Knoten im Graphen), die zu matchenden Daten, und die Knoten, zu dem Beziehungen und Eigenschaften zu retournieren sind.

Big Data Analytics

Hier sind wir bei einem meiner Lieblingsthemen angelangt. Hadoop, bzw. unter Microsoft Azure HDInsight. Dazu gibt es einen eigenen Blog-Eintrag, auf den ich an dieser Stell gerne verweisen möchte, da die Beschreibung von MapReduce, Hive und Pig dieses Mal den Rahmen sprengen würde.

Datenkonsistenz in NoSQL-Datenbanken

Da Daten zu mehreren Servern repliziert werden - einer verteilt die Daten an mehrere – stellt sich die Frage der Datenkonsistenz beim Zugriff auf ebendiese, da das Verteilen der Daten Zeit benötigt. Was passiert nun, wenn Clients auf diese Daten in der Zeit während die Synchronisation läuft zugreifen? Welche Daten sind denn nun für diese Clients sichtbar? In der NoSQL-Welt unterscheidet man zwischen Strong Consistency und Eventual Consistency. Aus der relationalen Welt sind wir Strong Consistency (read committed) gewohnt, das heißt alle Clients sehen immer die gleichen (korrekten) Daten, auch, wenn sofort – noch während das Schreiben der Daten läuft - gelesen wird.
Wenn das System nun nur Eventual Consistency unterstützt würden Clients alte Daten geliefert bekommen, da diese eventuell auf einen Server zugreifen, wo die Daten noch nicht synchronisiert wurden. Klingt absurd und unbrauchbar? Nun, ist es wirklich so wichtig in einem Blog die allerletzten Kommentare (sofort) zu sehen? Oder in einem FaceBook Beitrag? Man sieht also, es kommt ein wenig auf die Anwendung an, wofür das System eingesetzt wird.
Zwei weitere Consistency Level, die zwischen den beiden genannten liegen sind Bounded Staleness und Session. Ersterer garantiert konsistentes Schreiben mit verzögerten Lesezugriffen für alle Clients, letzterer garantiert die Datenkonsistenz für den aktiven Client für seine eigenen Lese- und Schreibzugriffe.
Wie sieht das unter Microsoft Azure aus?
· Azure Table Storage verwendet Strong Consistency, garantiert also die aktuellen Daten bei einem lesenden und schreibenden Zugriff durch mehrere, bzw. verschiedene Clients.
· Azure DocumentDB lässt uns die freie Wahl zwischen Strong, Bounded Stateless, Session, oder Eventual Consistency.
· HBase verwendet Strongly Consistent Lese- und Schreib-Zugriffe
Die Wahl des Consistency-Levels beeinflusst natürlich die Performance des Systems, wird Strong Consistency eingesetzt ist das die langsamste Variante für Lese- und Schreibzugriffe, Eventual Consistency die Schnellste.

 

RDBMS und/oder NoSQL?

Wie sollen nun neue Projekte entwickelt werden? Setzen wir in Zukunft NUR NOCH auf NoSQL? Fügen wir die NoSQL-Technologien erst einmal zu dem zuvor gezeigten Bild hinzu, so ergibt sich ein Gesamtbild und wir können uns überlegen, wie wir weiter vorgehen könnten:
clip_image008
NoSQL ist typischerweise eine gute Wahl, wenn es sich um unstrukturierte, bzw. Daten ohne festem Schema handelt. Das Schema muss nicht extra er- bzw. gefunden werden, Erweiterungen mit neuen Feldern sind– in Hinsicht auf die Speicherung - problemlos zu verarbeiten.
Eine denormalisierte Form der Daten wird nicht nur favorisiert, sondern ist sogar notwendig, da keinerlei Joins, zumindest in der Art und Weise wie diese in relationalen Systemen gehandhabt werden, Unterstützung finden.
Lösungen, die mittels NoSQL implementiert werden, lassen sich häufig einfach besser skalieren als relationale Datenbanklösungen. Das Hinzufügen weiterer Datenknoten ist einfach, die Daten werden meist repliziert, daraus ergibt sich in Summe auch einen besseren Schutz gegen Datenverlust.
Allerdings bieten die NoSQL-Lösungen nicht die Möglichkeit von ACID-Transaktionen über mehrere "Tabellen". Desweitern gilt natürlich, dass komplexe und/oder dynamische Abfragen, bzw. Berichte am besten von einem relationalen Datenbanksystem bedient werden. Die Abfragemöglichkeiten von NoSQL-Lösungen sind dafür einfach zu limitiert – was ja auch nicht im Sinne der Erfindung wäre.
Wie sehr häufig im Leben gibt es nicht das allgemein gültige Kochrezept, es muss also nicht immer das Eine oder das Andere sein. Für bestimmte Gesamtlösungen kann ich mir ohne Weiteres eine Hybrid-Form - die Kombination eines relationalen Systems mit einem NoSQL-System vorstellen.

Quellen

Relational Database: http://en.wikipedia.org/wiki/Relational_database
Cod’s 12 rules: http://en.wikipedia.org/wiki/Codd%27s_12_rules
CAP-Theorem: http://de.wikipedia.org/wiki/CAP-Theorem
NoSQL Datenbanksysteme: http://nosql-database.org/
FaceBook
 & HBase: https://www.facebook.com/publications/376874985754849/
Neo4J
 unter Azure: http://neo4j.com/blog/announcing-neo4j-on-windows-azure/

 

Zusammenfassung

Microsoft ist mit Azure und den angebotenen Storage-Möglichkeiten im NoSQL-Bereich meiner Meinung nach ein Vorreiter und Visionär. Hier wird Technologie geboten, die die meisten Unternehmen noch nicht ausreichend anzuwenden wissen. Die Möglichkeiten, die sich hier für Entwickler und Firmen bieten, um neue Applikationen und Dienste anbieten zu können, sind großartig und werden vermutlich erst in den nächsten Jahren ihr volles Potential entfalten. Wir alle müssen uns erst auf diese Technologien einlassen und ihnen auch das Vertrauen entgegen bringen, das diese meiner Meinung nach auch verdienen. Es sind nicht neue Technologien, da diese größtenteils schon jahrelang im Einsatz, aber der breiten Business-Community bisher verborgen geblieben sind. Microsoft leistet hier mit der Azure Plattform eine großartige Möglichkeit all diese Technologien zu testen und neue Visionen umzusetzen.

CSharpCodeFormatter