5 Stimmen

Leistung bei der Abfrage relationaler Daten mit dokumentenbasierter Nosql (mongodb, couchdb und riak, etc.)

Im Anschluss an meine Frage zu modellierung relationaler daten mit nosql Ich habe mehrere Artikel zu diesem Thema gelesen:

Nosql bedeutet nicht nicht-relational

Nosql Ecommerce Beispiel

Sie scheinen zu suggerieren, dass nosql normalisierte, relationale Daten verarbeiten kann.

Fahren wir also mit dem Beispiel von vorhin fort, einem CMS-System, das über zwei Datentypen verfügt: Artikel und Autoren, wobei der Artikel einen Bezug (per ID) zum Autor hat.

Im Folgenden sind die Vorgänge aufgeführt, die das System unterstützen muss:

  1. Abrufen eines Artikels nach ID zusammen mit dem Autor
  2. Alle Artikel eines bestimmten Autors abrufen
  3. Suche nach den ersten 10 Artikeln mit dem/den Autor(en), sortiert nach Erstellungsdatum

Ich würde gerne die Leistung dieser Vorgänge im Vergleich zu denselben Vorgängen verstehen, wenn die gleichen Daten in einem RDBMS gespeichert wären. Bitte geben Sie insbesondere an, ob die Operation MapReduce verwendet, mehrere Zugriffe auf den Nosql-Speicher (Links) erfordert oder vor dem Beitritt

Ich möchte die Diskussion auf folgende Punkte beschränken dokumentenbasiert nosql-Lösungen wie mongodb, couchdb und riak.

Bearbeiten 1:

Spring-Daten-Projekt ist auf Riak und Mongodb verfügbar

5voto

BigBlueHat Punkte 2339

Ich wollte nur eine CouchDB-Antwort für alle, die neugierig sind, einfügen :)

Wie bereits in der ersten Antwort erwähnt, ist es nicht ratsam, das Autorendokument in das Artikeldokument einzubetten. Daher gehen die folgenden Beispiele von zwei Dokumenttypen aus: Artikel und Autoren.

CouchDB verwendet MapReduce Abfragen, die typischerweise in JavaScript geschrieben werden (aber auch Python, Ruby, Erlang und andere sind verfügbar). Die Ergebnisse einer MapReduce Abfrage werden bei der ersten Abfrage in einem Index gespeichert und dieser Index wird für alle zukünftigen Abfragen verwendet. Änderungen an der Datenbank werden bei weiteren Abfragen in den Index aufgenommen.

Das CouchDB API ist komplett HTTP-basiert, d.h. alle Anfragen an die Datenbank sind HTTP Verben (GET, POST, PUT, DELETE) unter verschiedenen URLs. Ich werde sowohl die MapReduce Abfragen (in JavaScript geschrieben) als auch die URL auflisten, die verwendet wird, um die entsprechenden Ergebnisse aus dem Index abzufragen.

1. Abrufen eines Artikels nach ID zusammen mit dem Autor

Die einfachste Methode hierfür sind zwei direkte Dokumentenabfragen:

GET /db/{article\_id}
GET /db/{author\_id}

...wobei {author_id} der Wert ist, der sich aus dem Feld author_id des Artikels ergibt.

2. Alle Artikel eines bestimmten Autors abrufen

MapReduce

function (doc) {
  if (doc.type === 'article') {
    emit(doc.author_id, doc);
  }
}

GET /db/\_design/cms/\_view/articles\_by\_author?key="{author\_id}"

...wobei {author_id} die aktuelle ID des Autors ist.

3. Suche nach den ersten 10 Artikeln mit dem/den Autor(en), sortiert nach Erstellungsdatum

MapReduce

function (doc) {
  function arrayDateFromTimeStamp(ts) {
    var d = new Date(ts);
    return [d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds()];
  }

  var newdoc = doc;
  newdoc._id = doc.author_id;
  newdoc.created_at = arrayDateFromTimeStamp(doc.created_at);

  if (doc.type === 'article') {
    emit(newdoc.created_at, newdoc); 
  }
}

Es ist möglich, Include-ähnliche "Joins" in CouchDB zu erstellen, indem man ?include_docs=true in einer Ansichtsanfrage. Wenn Sie einen "_id"-Schlüssel in die Werteseite des emit (das zweite Argument) aufnehmen, dann wird das Hinzufügen von include_docs=true zu Ihren Abfrageparametern hinzufügen, wird das Dokument, auf das die angegebene "_id" verweist, einbezogen. Im obigen Fall ersetzen wir die eigene "_id" des Dokuments (die wir nicht mehr benötigen) durch die "_id" des Autors, auf den verwiesen wird (den Wert von "author_id" im Artikeldokument). Die Abfrage der 10 wichtigsten Artikel mit den zugehörigen Autoreninformationen sieht wie folgt aus:

GET /db/\_design/cms/\_view/articles\_by\_date?descending=true&limit=10&include\_docs=true

Bei der Abfrage dieser URL wird eine Liste der letzten 10 Artikel in einem ähnlichen Format zurückgegeben:

{"rows":\[
  { "id":"article\_id",
    "key":\[2011, 9, 3, 12, 5, 41\],
    "value":{"\_id":"author\_id", "title":"..."},
    "doc":{"\_id":"author\_id", "name":"Author Name"}
  }
\]}

Mit demselben Index können Sie eine Liste aller Dokumente in beliebiger Granularität (Jahr, Monat, Tag, Stunde usw.) mit oder ohne Autorendaten erhalten.

Es gibt auch Methoden für die Verwendung von View Collation, um mehrere Dokumente aus einem einzigen Dokument zusammenzufassen (wie eine Seite in einem CMS, die auf unterschiedliche Inhalte verweist). In diesen Folien, die ich für die CouchConf im Juli erstellt habe, gibt es einige Informationen darüber, wie man das macht: http://www.slideshare.net/Couchbase/couchconfsfdesigningcouchbasedocuments

Wenn Sie weitere Fragen haben, lassen Sie es mich bitte wissen.

4voto

Gates VP Punkte 44457

Abrufen eines Artikels nach ID zusammen mit dem Autor

SQL :

  • 1 Abfrage
  • 2 Indexabfragen
  • 2 Datenabfragen
  • zurückgegebene Daten = Artikel + Autor

MongoDB :

  • 2 Rückfragen
  • 2 Indexabfragen
  • 2 Datenabfragen
  • zurückgegebene Daten = Artikel + Autor

Alle Artikel eines bestimmten Autors abrufen

SQL :

  • 1 Abfrage
  • 1 Indexnachschlag
  • N Datenabfragen
  • zurückgegebene Daten = N Artikel

MongoDB :

  • 1 Abfrage
  • 1 Indexabfrage
  • N Datenabfragen
  • zurückgegebene Daten = N Artikel

Suche nach den ersten 10 Artikeln mit dem/den Autor(en), sortiert nach Erstellungsdatum

SQL :

  • 1 Abfrage
  • 2 Indexabfragen
  • 11 bis 20 Datenabfragen (Artikel und dann eindeutige Autoren)
  • zurückgegebene Daten = 10 Artikel + 10 Autoren

MongoDB :

  • 2 Abfragen ( articles.find().sort().limit(10) , authors.find({$in:[article_authors]})
  • 2 Indexabfragen
  • 11 bis 20 Datenabfragen (Artikel und dann eindeutige Autoren)
  • zurückgegebene Daten = 10 Artikel + 1 bis 10 Autoren

Zusammenfassung

In zwei Fällen erfordert MongoDB eine zusätzliche Abfrage, erledigt aber den größten Teil der Gesamtarbeit darunter. In einigen Fällen gibt MongoDB weniger Daten über das Netz zurück (keine wiederholten Einträge). Die Abfragen zur Verknüpfung werden in der Regel dadurch eingeschränkt, dass alle zu verknüpfenden Daten auf demselben Rechner liegen müssen. Wenn sich Autoren und Artikel an verschiedenen Orten befinden, müssen Sie am Ende sowieso zwei Abfragen durchführen.

MongoDB neigt dazu, eine bessere "rohe" Leistung zu erzielen, da nicht bei jedem Schreibvorgang ein Flush auf die Festplatte erfolgt (es handelt sich also eigentlich um einen Kompromiss in Bezug auf die "Haltbarkeit"). Außerdem hat es einen viel kleineren Query Parser, so dass es weniger Aktivität pro Abfrage gibt.

In Bezug auf die grundlegende Leistung sind diese Dinge sehr ähnlich. Sie gehen lediglich von unterschiedlichen Annahmen in Bezug auf Ihre Daten und die von Ihnen gewünschten Abwägungen aus.

2voto

Travis Punkte 10284

Für MongoDB würden Sie keine eingebetteten Dokumente für den Autorendatensatz verwenden. Der Pre-Join entfällt also, es sind mehrere Zugriffe auf die DB erforderlich. Sie können jedoch den Autor in den Cache stellen und müssen diese zweite Abfrage nur einmal für jeden Datensatz durchführen. Die von Ihnen angegebenen Abfragen sind in MongoDB ziemlich trivial.

var article = db.articles.find({id: article_id}).limit(1);
var author = db.authors.find({id: article.author_id});

Wenn Sie ein ORM/ODM verwenden, um Ihre Entitäten innerhalb Ihrer Anwendung zu verwalten, wäre dies transparent. Es wären allerdings zwei Zugriffe auf die Datenbank. Die Antworten sollten jedoch schnell sein, zwei Zugriffe sollten überhaupt nicht auffallen.

Die Suche nach Artikeln eines bestimmten Autors ist einfach umgekehrt...

var author = db.authors.find({id: author_name}).limit(1);
var articles = db.articles.find({author_id: author.id});

Also wieder zwei Abfragen, aber der Abruf durch einen einzigen Autor sollte schnell sein und kann leicht zwischengespeichert werden.

var articles = db.articles.find({}).sort({created_at: 1}).limit(10);
var author_ids = articles.map(function(a) { return a.author_id });
var authors = db.authors.find({id: { '$in': authors_ids }});

Zum Schluss noch einmal zwei Abfragen, die aber ein wenig komplexer sind. Sie können diese in einer Mongo-Shell ausführen, um zu sehen, wie die Ergebnisse aussehen könnten.

Ich bin mir nicht sicher, ob es sich lohnt, dafür eine Map Reduce zu schreiben. Ein paar schnelle Roundtrips könnten etwas mehr Latenz haben, aber das Mongo-Protokoll ist ziemlich schnell. Ich würde nicht übermäßig besorgt darüber sein.

Und schließlich die tatsächlichen Auswirkungen auf die Leistung, wenn man es auf diese Weise macht... Da Sie idealerweise nur indizierte Felder im Dokument abfragen, sollte es ziemlich schnell gehen. Der einzige zusätzliche Schritt ist ein zweiter Round Trip, um die anderen Dokumente zu holen, aber je nachdem, wie Ihre Anwendung und Datenbank strukturiert ist, ist das wahrscheinlich keine große Sache. Sie können Mongo anweisen, nur Abfragen zu profilieren, die einen bestimmten Schwellenwert überschreiten (standardmäßig 100 oder 200 ms, wenn aktiviert), so dass Sie im Auge behalten können, was Ihr Programm bei wachsenden Datenmengen an Zeit benötigt.

Der einzige Vorteil, den Sie hier haben und den ein RDMS nicht bietet, ist die viel einfachere Zerlegung von Daten. Was passiert, wenn Sie Ihre Anwendung über CMS hinaus erweitern, um andere Dinge zu unterstützen, aber denselben Authentifizierungsspeicher verwenden? Es handelt sich jetzt zufällig um eine völlig separate DB, die von vielen Anwendungen gemeinsam genutzt wird. Es ist viel einfacher, diese Abfragen datenbankübergreifend durchzuführen - bei RDMS-Speichern ist das ein komplexer Prozess.

Ich hoffe, dies hilft Ihnen bei Ihrer Entdeckung von NoSQL!

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X