Einführung

Der Begriff NoSQL ist irreführend, da „No“ nicht einfach mit „kein“ gleichzusetzen ist. Für die meisten steht es nämlich für „Not only SQL“ – also nicht für ein Ende der Abfragesprache SQL. Kurz gesagt beinhaltet dieser Trend alle Arten von Datenspeichern, die eben nicht den relationalen Datenbanken (RDBMS) zuzuordnen sind. Häufig sind sie explizit für ein verteiltes Arbeiten mit großen bis sehr großen Datenmengen ausgelegt, bieten diesem Einsatzzweck entsprechende Datenstrukturen und verzichten bewusst auf Transaktionen. Aber in Bezug auf das Datenmodell, die Unterstützung dynamischer Abfragen, die Verteilung und den Persistenzmechanismus unterscheiden sie sich erheblich voneinander: „NoSQL“ ist eine sehr breite und unscharfe Rubrik, die alle Varianten von Textdateien bis hin zu objektorientierten Datenbanken beinhaltet.

Wann also entscheiden wir uns gegen Oracle & Co. und suchen nach Alternativen im NoSQL-Angebot? Nicht jeder hat es mit den unüberschaubaren Datenmassen der Internet-Vorzeigeunternehmen zu tun und damit tatsächlich ein Skalierungsproblem, das mit RDBMS-Mitteln allein nicht zu lösen ist. Vielfach ist nur das relationale Datenmodell ein Dorn im Auge, weil es nicht den Applikations-Objekten mit ihren Beziehungen und Abhängigkeiten entspricht und daher komplizierte Wege (ORM, Bypass-SQL) beim Speichern und Abfragen erfordert. Wieder andere finden die Veränderbarkeit der „Schemas“, ohne Tabellen-Migration, wie es dokumentenorientierte Datenbanken bieten, wichtig. Die tolerante Ausfallbehandlung sowie die Möglichkeit, bei Laständerungen flexibel zu skalieren (up- und downsize), finden wiederum alle gut.

Relationale Datenbanken – ergraute Platzhirsche?

Um die vermeintlich unterschiedlichen Welten besser zu verstehen, folgt ein kurzer Überblick des relationalen Systems: Alle Daten sind zeilenweise (Tupel) in Tabellen abgelegt, deren Spalten starr definiert sind (beispielweise Integer, indiziert und eindeutig). Streng genommen werden Entitäts-Beziehungen, bis auf Views oder Integritätsregeln (Fremdschlüssel), nicht im Datenmodell hinterlegt und erst zur Laufzeit der Abfrage definiert. Mehrere Aktivitäten sind in konsistenten Transaktionen zusammenfassbar und eigene Logik kann direkt auf der Datenschicht platziert werden (Trigger, PLSQL usw.). In der Praxis werden die Regeln zur Datenintegrität (Constraints) oft noch redundant implementiert, beispielsweise um Formulareingaben im Client (Browser/JavaScript) und auf dem Server prüfen zu können.

Mit dem NoSQL-Hype kam die Behauptung auf, dass relationale Datenbanken aufgrund historisch-konzeptioneller Schwächen nicht gut skalieren. Für die Enterprise-Kundschaft – also da, wo nicht wie bei jungen Start-ups auf jeden Euro geguckt werden muss – finden wir bereits seit einiger Zeit Cluster/Partitioning-Lösungen von Microsoft bis MySQL und auch Shared-Nothing-Systeme (eigenständige Hosts), vor allem im Data-Warehouse-Bereich (Aster Data, Greenplum, Neteeza). Gerade jedoch im Webumfeld, wo wir häufig auf die populären „freien“ Datenbanken MySQL und PostgreSQL stoßen, wurden in der Vergangenheit Lösungen um das eigentliche Problem herum geschaffen. Die obligatorische Primary/Replica-Architektur bei lese-anfälligen Webseiten wurde durch brachiales denormalisieren des Entity-Relationship-Modells (ERM) – einhergehend mit massiven RAM-Caches (JBoss Cache, Memcached usw.) – ausgebaut.

Tabelle 1: Überblick zur NoSQL-Klassifikation
Schlüssel–Wert Spalten Dokument Graph
Aufbau Collections von Schlüssel-Wert-Paaren Spalten und Spaltenfamilien. Zugriff erfolgt direkt auf die Spaltenwerte (einzeln oder aggregiert) Schlüssel-Wert-Paare - mit dem Zusatz, dass die Datenstruktur im Value interpretiert wird Fokus auf Datenverbindungen und schnelles Durchwandern dieser Beziehungen
Skalierbarkeit/ Performance +++ +++ ++ ++
Komplexität + ++ ++ +++
Inspiration und Verwandtschaft Berkeley-DB, Memcache Sybase IQ, BigTable Lotus Notes Graphen-Theorie
NoSQL- Vertreter  Voldemort, Redis, Riak Hbase, Cassandra, Hypertable CouchDB, MongoDB, Redis Sones, Neo4j, InfoGrid
// Datenmodell über die Keys
'User:bob:name' = 'Bob'
'User:bob:friends' = 'jim;eva'
'User:jim:friends' = 'bob'

// Inkrementieren (Redis)
myDb.set("users_counter", 0)
myDb.incr("users_counter")
myDb.get("users_counter")
Listing 1: Beispiel für einen Schlüssel-Wert-Datenspeicher
User {
    bob : {
	    { "name": "username", "value": "Bob", "timestamp": 123456789 },
	    { "name": "email", "value": "[email protected]", "timestamp": 123456789 }
	},
    jim : {
	    { "name": "username", "value": "Jim", "timestamp": 123456789 }
    }
}

// Select
db.get("column_family", key, "column") // Cassandra
db.get(key, "column_family:column") // HBase

// Create über eine Beispiel-API
USER.insert('bob', {'id': 'bob', 'name': 'Bob'})
USERNAME.insert('Bob', {'id': 'bob'})
FRIENDS.insert('bob', {'jim': time()})
FRIENDS.insert('jim', {'bob': time()})
Listing 2: Beispiel für einen spaltenorientierten Datenspeicher (Pseudo-Code)
[
	{
		_id: 'bob', type: 'user', name: 'Bob',
		friends: [{id: 'jim'}, {id: 'eva'}]
	},
	{
		_id: 'jim', type: 'user', name: 'Jim',
		friends: [{id: 'bob'}]
	}
]
Listing 3: Beispiel für einen Dokumenten-Speicher
Node bob = myDb.createNode()
bob.setProperty('username', 'Bob')
Node jim = myDb.getNodeById('jim')
bob.createRelationshipTo(jim, RelTypes.FRIENDSHIP)

for (Relationship rel : bob.getRelationships(FRIENDSHIP)) {
	Node friend = rel.getOtherNode(); // Node: jim
}
Listing 4: Beispiel für eine Graphen-Datenbank (Pseudo-Code)

Neue Anforderungen

Performance und (flexible) Skalierbarkeit

Bei den RDBMS müssen diese Anforderungen entweder teuer eingekauft werden, oder durch eine in der Praxis schwierig zu handhabende Sharding-Architektur (oft eigen-programmiert), die Daten einer Entität über mehrere Schemas und Tabellen hinweg verteilt, umgesetzt werden. NoSQL-Vertreter, wie Riak oder MongoDB, verfügen über intelligente Versionierungs- und Replikationsmechanismen, um auch bei Ausfällen oder dem Hinzufügen von Hosts (Nodes) keine Daten zu verlieren.

Mit zwei – inzwischen zu Buzzwords gewordenen – Begriffen werden Sie in Berührung kommen: das CAP-Theorem [1][2] und – damit verbunden – Eventual Consistency (ACID-Gegenstück: BASE) [3], als Ziel-Modell der meisten dieser Produkte. Während herkömmliche Datenbanken auf Konsistenz und Verfügbarkeit ausgerichtet sind (CA aus CAP), legen viele NoSQL-Datenspeicher ihren Fokus auf Verfügbarkeit und Partitionstoleranz (AP aus CAP) [4].

Somit verzichten sie sehr bewusst auf den Overhead, der durch Speichermanagement, Transaktionsprotokollierung und die Schwierigkeiten des zuvor angesprochenen Daten-Sharding (Applikations-Daten, Locks und Indizes, unterschiedlich wachsende Shard-Segmente) entsteht.

Bessere Datenstrukturierung

Zu Beginn einer NoSQL-Evaluierung sollten Sie nicht nur die zu erwartende Datenmenge, sondern auch die Art der Daten berücksichtigen. Ist der Zugriff mehrheitlich spalten-bezogen (Beispiel: OLAP) oder wollen Sie besondere Beziehungen (z. B. Baum- und Netzwerkstrukturen) abbilden oder aber ohne Migrationsaufwand neue Datenfelder hinzufügen können, so gibt es dafür besser geeignete Lösungen.

Im nächsten Schritt folgt dann die Frage, wie Sie auf Ihre Daten zugreifen wollen: ad hoc oder benutzt Ihre Anwendung immer dieselben Abfragen? Brauchen Sie stets die aktuellsten Ergebnisse oder können Sie mit Wartezeit bzw. vorberechneten Werten leben?

Bei SQL-Datenbanken werden Beziehungen über Joins zum Zeitpunkt der Abfrage definiert. Die Non-RDBMS bieten diese Flexibilität im Allgemeinen nicht. Der spätere Zugriff muss im Voraus, entweder im Datenmodell oder mittels Hilfsfunktionen für Selektion und Verarbeitung, berücksichtigt werden. Auf der anderen Seite können zusammengehörende Daten in NoSQL-Speichern (je nach Modell) gemeinsam abgelegt und auch en bloc gelesen werden. Bei relationalen Datenbanken erfolgt hier unter Umständen ein aufwendiger Join über viele Tabellen.

Auswahl des optimalen Datenspeichers

In der Planungsphase neuer Projekte wird oft der Einsatz von unbekannten Programmiersprachen oder Frameworks evaluiert, während die Auswahl des Datenbanksystems aus betriebswirtschaftlichen Gründen (Lizenzen, Support, Infrastruktur) oder Vorlieben der Entwickler und Administratoren selten infrage gestellt wird.

Eine von der NoSQL-Diskussion losgelöste Beurteilung eines Datenbanksystems würde sich mit dem Lizenzmodell (kommerziell vs. Open Source), dem Reifegrad, dem gebotenen Funktionsumfang und sicherlich auch mit der CAP-Gewichtung beschäftigen. Hinzu kommen noch Fragen zur erforderlichen Systemlandschaft (Betriebssystem, APIs, Abhängigkeiten) und ob Tools für Anwender und Administratoren verfügbar sind.

Statt also möglichst viele Speichersysteme in eine unübersichtliche Feature-Matrix zu packen, konzentrieren wir uns hier auf typische Einsatzfälle, wie Sie diese vielleicht bei künftigen Projekten vorfinden.

Das soll natürlich nicht heißen, dass Ihre gesamte nächste Anwendung auf einem Schlüssel-Wert-Speicher aufgebaut sein muss, nur weil ein Teil Ihres Datenmodells dafür prädestiniert wäre. Vergessen Sie daher nicht, dass viele Non-RDBMS ihre Stärken in besonderen, sehr spezifischen Einsatzszenarien zeigen und Sie deren Verwendung immer auch im Kontext Ihrer Anwendung beurteilen sollten. Häufig kann auch eine Kombination aus verschiedenen Systemen sinnvoll sein, obwohl Sie dabei natürlich den Mehraufwand für Entwicklung und Administration berücksichtigen müssen.

Domänenspezifisches Datenmodell

Nehmen wir einen URL-Kürzungsdienst (z. B. bit.ly oder is.gd), wie er bei Twitter oft eingesetzt wird. Ohne Frage ist das Datenmodell überschaubar und auch in einer SQL-Datenbank leicht umsetzbar. Werfen wir einen Blick in die NoSQL-Ecke, würde uns ein Schlüssel-Wert- oder ein Dokumenten-Speicher, der die Kurz-Urls als Schlüssel und zugehörige Eigenschaften im „Value“ bzw. Dokument ablegt, passend erscheinen.

Hierarchische Daten oder Baumstrukturen betreffen meistens nur einen Teil des Datenmodells und können sowohl in einem RDBMS (beispielsweise Nested Sets) als auch in einem Dokumenten-Speicher abgelegt werden.

Für Produkt- oder Freundschafts-Empfehlungen, optimale Routen („Problem des Handlungsreisenden“) und andere Fragestellungen, bei denen das Traversieren über netzwerkartige Beziehungen erforderlich ist, sind Graphen-Datenbanken die beste Wahl. Zwar lassen sich diese komplexen Beziehungen auch in einem RDBMS über Rekursion (Oracle bspw.: connect by *) und explizite Indizes abfragen, dennoch liefern Systeme wie neo4j oder Sones Vorteile bei Performance und Simplifizierung.

Flexible Datenstrukturen erwünscht

Will man in einem relationalen System flexibel sein, muss zwangsläufig auch das Datenmodell darauf ausgelegt sein. Eine Möglichkeit ist der EAV-Ansatz (engl. Entity-Attribute-Value), welcher die Werte in eigenen Datensätzen über mehrere Tabellen (Datentypen, Metadaten) verteilt speichert.

Für manchen Entwickler oder Administrator ist die beste Errungenschaft, dass viele der NoSQL-Vertreter kein starres Schema, wie wir es von den SQL-Datenbanken kennen, besitzen (engl. Schema-less). Dadurch können neue Datenfelder beliebig hinzugefügt werden und die oft schmerzhafte Migration, vor allem bei Millionen von bestehenden Datensätzen (Deployment von Applikation und Datenbankänderungen, Indizes usw.), entfällt.

Bekannte Einsatzgebiete sind CMS-/CRM-Systeme oder Issue-Tracker, in denen neue Attribute durch einen Applikations-Administrator oder angedockte Plug-ins erzeugt werden können.

Historische Daten

Es kann Bestandteil der Anwendung (Revisionen) oder eine Festlegung aus Gesetzen und Zertifikaten (Audits) sein, dass die Datenhistorie protokolliert werden muss.

In einem RDBMS könnten Sie einfach die Tabellenstruktur duplizieren und über Callbacks (Trigger o.ä.) die Daten nach jeder änderung archivieren. Je nach Objekt-Komplexität und involvierter Tabellen funktioniert das so lange gut, bis die erste Änderung an der Datenstruktur ansteht. Den Regeln entsprechend, wann eine Archivkopie erzeugt wird, wachsen die Daten stetig an, was das Nachziehen der Schema-Anpassungen zusätzlich erschwert.

Aufgrund der Flexibilität gegenüber Schema-Änderungen eignen sich die Dokumenten-Speicher, wie CouchDB oder MongoDB, sehr gut für diesen Einsatz. Allerdings gilt auch hier, dass änderungen in der Geschäftslogik über die Archiv-Versionen mitgetragen werden müssen. Außerdem ist es (initial) viel aufwendiger, wenn zeitliche Auswertungen (z. B. Veränderung des Produkt-Preises) vorgenommen werden sollen. Denkbar ist auch, eine SQL-Datenbank zu verwenden, die beispielsweise täglich in einen Dokumenten-Speicher übertragen wird. Über sogenannte Views (Map/Reduce-Extraktion) kann dann ein gewaltiger Performance-Gewinn für z. B. Analyse-Aufgaben oder die schnelle Zusammenführung mit den Tagesdaten erreicht werden.

Datentransformation

Bei Bulk-Imports, umfangreichen ETL-Aufgaben oder wenn in großen Mengen Daten gesammelt werden, die erst später verarbeitet werden sollen, bieten sich Systeme an, die schnell und flexibel horizontal skalieren können (zusätzliche Hosts). Im Transformationsprozess werden dann bspw. Berechnungen vorgenommen oder Schlüsselwerte eindeutig gemacht. Im Analyse-Bereich führen Dimensions-Betrachtungen (Zeit, Kategorien usw.) auf großen Datenmengen oft zu Performance-Problemen. Das Hadoop-Komponenten-Framework (Hbase u.a.) bietet wertvolle Tools für die Verarbeitung extrem großer Datenmengen.

Unkritische, aber massiv wachsende Daten

Unkritisch bedeutet hier, dass wir eine solch gewaltige Menge von Daten schreiben, dass die zeitversetzte Verfügbarkeit (über alle Hosts) oder gar der Verlust (Fehlerfall) einen akzeptablen Kompromiss bedeuten. Prominente Beispiele sind die Newsfeeds der Social Networks oder View-Counter bei z. B. Werbe-Bannern, die in 1000er Blöcken verrechnet werden.

Bei Ihrer Evaluierung sollten Sie vor allem die für massive Schreibvorgänge optimierten Systeme (z. B. MongoDB oder Redis) in Betracht ziehen.

Kurzlebige Daten

Dieses Szenario betrifft Daten mit einem absehbaren Haltbarkeitsdatum. Gerade für Session- und Anwendungs-Caches bieten sich reine RAM-Speicher (u. a. Memcached) an. Sind die Daten stark zeitbezogen (z. B. Log-Daten) und sollten diese in gewissen Abständen doch auf die Festplatte gesichert werden, kommen Systeme wie Redis oder Cassandra ins Spiel. Diese speichern zu ihren Wertepaaren auch immer noch einen Timestamp und bieten optimierte Operationen, um nur den gewünschten Zeitbereich betrachten oder entfernen zu können.

Die Git-Hosting-Plattform github hat einen eigenen Dienst [5] für die Verarbeitung von Background-Jobs (Queues) entwickelt, weil keine der existierenden Lösungen ihren Anforderungen entsprach. Dieser Dienst basiert auf Ruby sowie einem Redis-Datenspeicher und zeigt, dass auch die Neu-Implementierung von Best-Practice-Ansätzen ein Anwendungsfall sein kann.

Sicherheit (sensible Daten)

Bei Webanwendungen gibt es oft nur einen Datenbanknutzer, dem meist auch alle Rechte („grant all“) zugeordnet wurden. Dabei bieten viele RDBMS sehr feingliedrig einstellbare Rechte, die von Verwaltungsaufgaben bis hin zu ausführbaren Kommandos (bis auf Datensatzebene) definiert werden können.

Die meisten NoSQL-Vertreter (auch hier gibt es Ausnahmen, wie CouchDB und OrientDB) bieten keine integrierten Möglichkeiten der benutzer- oder gar rollenbasierten Rechteverwaltung und sind oft nur über vorgelagerte Systemschichten (z. B. Loadbalancer einer HTTP-API) absicherbar.

Da kein SQL mehr verwendet wird, gehört auch SQL-Injection der Vergangenheit an – oder? Selbst wenn die Argumentation etwas hinkt, so ist es aufgrund fehlender Standards und fehlenden Implementierungswissens weitaus schwerer für einen potenziellen Angreifer. Benutzereingaben sollten dennoch weiterhin nicht blind an die Datenspeicher-API weitergegeben werden (z. B. $where in MongoDB).

Ad-hoc-Abfragen: SQL vs. Map/Reduce

Klar, in den meisten IT-Berufen ist SQL durch Studium und Praxis bekannt. Dennoch gibt es auch in vielen nichttechnischen Positionen (Analysten, Projektleiter usw.) Fachaufgaben (Auswertungen, Korrekturen usw.), die nur mittels SQL-Kenntnissen und ERM-Verständnis gelöst werden können.

Die Non-RDBMS bieten in der Regel nur schwache Unterstützung für Ad-hoc-Abfragen. Viele erfordern das Schreiben von Map/Reduce-Algorithmen, um Daten zu extrahieren. Einige bieten allerdings auch Indizes, temporäre Views (Couch-DB) oder eine proprietäre Abfragesprache (MongoDB oder Sones-GQL). Unterschiedlich implementierte APIs oder integrierte 3rd Party Index/Search Engines (Riak Search mittels Lucene) sind daher Normalität.

Transaktionen und wertvolle Daten

Beim klassischen Lehrbeispiel – Geld wird von einem Konto auf ein anderes transferiert – spielen die Konsistenz-Garantien, welche die meisten RDBMS bieten, eine herausgehobene Rolle. Nun gibt es ACID-Transaktionen auch bei einigen NoSQL-Speichern, ein großer Teil erreicht seine Vorteile (Skalierung, Geschwindigkeit) jedoch nur, indem die Konsistenz-Anforderungen aufgeweicht werden (Eventual Consistency).

Wenn Sie aufgrund von geschäftskritischen Daten auf Transaktionen angewiesen sind, müssen Sie sich zwangsläufig der „Schneller vs. sicher“-Frage widmen und möglicherweise einem ausgereiften RDBMS den Vorzug geben.

Synchronisation von verteilten Daten

Das in CouchDB enthaltene, sehr robuste Replikations-Feature ist nicht nur für horizontal aufgesetzte Datenserver von Nutzen. Auch bei vorübergehend offline arbeitenden Anwendungen, die bei wiederkehrender Internetverbindung ihre Daten synchronisieren, finden wir einen Anwendungsfall. Kalender, Kontaktlisten oder Bookmarks (u. a. in Ubuntu) sind Beispiele, bei denen verschiedene Clients (Desktop, Webbrowser, Smartphone-Apps) zum Einsatz kommen, die zeitversetzt (Internetverbindung, Sync-Häufigkeit, leerer Akku) abgeglichen werden müssen. Für die Android-Entwicklung wurde kürzlich eine spezielle Version von CouchDB (CouchOne) veröffentlicht.

Fazit

Es kommt also auf viele Faktoren an, wenn Sie vor der Entscheidung für ein bestimmtes Datenspeichersystem stehen. NoSQL ist ein Mix aus überwiegend neuen Produkten mit bekannten Ideen und Architekturen: wie elastischer Skalierung, ausfallsicherer Verteilung oder alternativen Datenmodellen. Die Produkte sind oft kostengünstiger und werden aktiver weiterentwickelt, vor allem die Open-Source-Projekte.

Für den Einsatz in mittleren und großen Unternehmen sind die Produktanbieter (viele davon Start-ups) noch nicht breit und professionell genug aufgestellt und es mangelt häufig an qualifizierten Entwicklern und Beratern mit Erfahrungswissen. Ob berechtigt oder nicht, wird den NoSQL-Datenbanken im Enterprise-Umfeld nur wenig Vertrauen (Betriebssicherheit) entgegengebracht und der Reifegrad infrage gestellt. Interessant ist auch, dass viele IT-Fachleute [6] noch nie etwas von NoSQL gehört haben oder es generell ablehnen.

Die SQL-Datenbanken werden auch nach 30-jährigem Jubiläum nicht vom Thron gestoßen. Sie sind bei Ad-hoc-Abfragen flexibler und durch die SQL-Standardisierung auch von einer breiteren Benutzerschaft (Skills, Tools, Integrationen) einsetzbar. Gerade die großen Anbieter sind seit vielen Jahren auf dem Markt vertreten und millionenfach im Einsatz. Weiterhin ist erwiesen, dass sich damit nahezu alle Datenstrukturen mehr oder weniger aufwendig speichern und wieder auslesen lassen. Für OLTP-Anwendungen, bei denen es also um zuverlässige Transaktionen geht, ist ein ausgereiftes RDBMS immer auch eine sichere Entscheidung. Dennoch, auch wenn hier im Moment Alternativen entwickelt werden (Xeround oder VoltDB), sind sie nicht die beste Wahl, wenn nach schnellen, ausfallsicheren, elastisch skalierenden – aber auch bezahlbaren Lösungen – gesucht wird.

Werfen Sie trotzdem nicht sofort Ihre relationale Datenbank über Bord und vertrauen blindlings auf die neuen Systeme, die vielleicht nur in Nischen gut funktionieren oder durch übereifrige Blogger zu einem Allheilmittel stilisiert wurden. Was also sollen Sie nun tun? Der Rat kann hier nur sein: Probieren Sie es aus und „spielen“ Sie damit herum. Starten Sie mit vermeintlich einfacheren Systemen, die gut dokumentiert sind und vielleicht Gemeinsamkeiten aus der relationalen Welt mitbringen. Die jahrelange Arbeit mit den RDBMS (Design, SQL, Administration) hat uns geprägt und ist nicht einfach umzuwerfen.

Es ist so ein bisschen, wie das TV-Experiment Oliver Pocher und Harald Schmidt gemeinsam in einer Sendung. Beide haben ihre Stärken und Schwächen, Fans und Zielgruppen … und so ist es auch bei der neuen Datenspeicher-Generation. Warum also nicht neue Wege beschreiten und eine SQL- mit einer No-SQL-Datenbank kombiniert in einer Anwendung einsetzen?

Literatur und Links

  1. E. A. Brewer, Towards Robust Distributed Systems, in: Symposium on Principles of Distributed Computing (PODC), 2000, http://www.cs.berkeley.edu/~brewer/cs262b-2004/PODC-keynote.pdf  ↩

  2. S. Gilbert, N. Lynch, Brewer’s Conjecture and the Feasibility of Consistent, Available, Partition–Tolerant Web Services, http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.20.1495&rep=rep1&type=pdf  ↩

  3. W. Vogels, Eventually Consistent – Revisited, 2008, http://www.allthingsdistributed.com/2008/12/eventually_consistent.html  ↩

  4. N. Hurst, Visual Guide to NoSQL Systems, 15.3.2010, http://blog.nahurst.com/visual-guide-to-nosql-systems  ↩

  5. Ch. Wanstrath, Introducing Resque, 3.11.2009, http://github.com/blog/542-introducing-resque  ↩

  6. Ch. Babcock, Surprise: 44 % Of Business IT Pros Never Heard Of NoSQL, 18.9.2010, http://www.informationweek.com/news/business_intelligence/databases/showArticle.jhtml?articleID=227500077  ↩