Seitenanfang

Wechsel von SQL zu NoSQL

Es gibt einen ganz sicheren Weg, die ersten NoSQL-Versuche in einem Fiasko enden zu lassen: MongoDB als 1:1 Ersatz für MySQL verwenden. Der Wechsel zu einer NoSQL-Datenbank ist nicht einfach nur ein Wechsel des Datenspeichers, sondern erfordert auch eine grundlegend neue Arbeits- und Denkweise.

von_mysql_zu_mongodb.jpgAuf den ersten Blick lassen sich Tabellen ganz einfach durch Collections und Datensätze durch Dokumente ersetzen und tatsächlich lässt sich eine NoSQL-Datenbank auch genau so verwenden, aber spätestens beim ersten nicht mehr funktionierenden SELECT ... JOIN scheinen MongoDB wichtige grundlegende Datenbankfunktionen zu fehlen. Tatsächlich fehlen dokumentenbasierten Datenbanken viele Features relationaler Datenbanken, eben weil sie nicht relational arbeiten.

Bereits die Benutzerverwaltung des neuen Projektes bietet jede Menge mögliche (Denk-)Fehlerquellen. Mit MySQL bietet sich ein Konzept mit drei Tabellen an:

  • Eine User - Tabelle, die Benutzer-ID, Benutzername und Passwort enthält, sowie möglicherweise weitere relevante Stammdaten.
  • Eine UserAccess - Tabelle, die für jeden Benutzer eine oder mehrere Zeilen enthält, jeweils bestehend aus der Benutzer-ID und der erteilten Berechtigung.
  • Eine UserLogins - Tabelle, in der bei jedem Login eines Benutzers eine Zeile mit Benutzer-ID, Zeitstempel und IP-Adresse hinzugefügt wird.

In der NoSQL-Welt wäre eine solche Konstruktion aus der Collections überflüssig und unnötig kompliziert. Hier reicht eine einzige User - Collection aus:

  • Der Schlüssel _id enthält automatisch eine nicht-fortlaufende Benutzer-ID.
  • Die Schlüssel username und password enthalten die Zugangsdaten. Weitere Stammdaten werden nach Bedarf einfach mit eigenen Schlüsseln abgelegt.
  • Der access - Schlüssel enthält ein Hash, in dem jede erteilte Berechtigung als Schlüssel fungiert und damit schnell und direkt angesprochen werden kann. Als Wert bietet sich wahlweise eine 1 bzw. "true" an - oder auch eine Information zum Ersteller, Erteilungszeitpunkt oder Ablauf. Die Schlüssel könnten sogar direkt hierarchisch abgelegt werden, in dem die einzelnen Werte selbst wieder Hashes beinhalten.
  • Der logins - Schlüssel beinhaltet ein Array, an das bei jedem Login ein neues Element bestehend aus Zeitpunkt und IP-Adresse angehängt wird.

Bei beiden Varianten empfiehlt es sich, den Benutzernamen mit einem eindeutigen (unique) Index zu belegen, damit kein Benutzername doppelt vergeben wird und der Zugriff auf dieses bei Anmeldeversuchen eines Benutzers üblicherweise als Schlüssel dienende Feld möglichst schnell abläuft.

Bei MySQL würde die Applikation zunächst mittels SELECT id FROM User WHERE username=? AND passwort=? den passenden Datensatz zu einer Benutzereingabe suchen, um im Erfolgsfall die passenden Zeilen der UserAccess - Tabelle nachzuladen. Bei einem JOIN der Tabelle von Anfang an würde zwar der zweite SELECT wegfallen, aber alle Benutzerdaten würden einmal pro erteiltem Zugriffsrecht übertragen werden.

MongoDB würde mittels db.User.find({ username: "...", password: "..." },{ _id: 1, access: 1 }) ebenfalls gezielt nach dem richtigen Benutzer suchen, allerdings gleich dessen ID und Zugriffsrechte zurückliefern - die zweite Abfrage entfällt und auch das - im Laufe der Zeit möglicherweise recht groß gewachsende - logins - Array würde nicht zurückgeliefert werden.

Beim Protokollieren des neuen Logins sind beide Lösungen ähnlich: MySQL würde mittels INSERT INTO UserLogins(user_id, timestamp, ip) VALUES(?,?,?) eine neue Zeile schreiben, während MongoDB mit einem db.User.update({ _id: "..." }, { $push: { logins: { timestamp: "...", ip: "..." }}}); das Array erweitern würde.

Die MongoDB-Befehle sind hier angegeben, wie sie in der Konsole geschrieben werden müssten - im Gegensatz zu SQL akzeptieren die MongoDB-Treiber die in der verwendeten Programmiersprache üblichen Datenstrukturen direkt und werden über Befehle bzw. OOP-Methoden angesprochen, anstatt wie SQL einen (möglicherweise zusammengesetzten) Text zu verwenden, der vom Server zunächst zerlegt und ausgewertet werden muss. Beispielsweise in Perl:

$user_collection->update(
{ _id => $id },
{ '$push' =>
{ logins => { timestamp => time, ip => $remote_addr } }
}
);

Das bei MySQL durchaus vorhandene Risiko von SQL-Injections ist bei dieser Lösung vollkommen ausgeschlossen, weil die Daten immer direkt als Variablen übergeben und nicht in eine $sql - Variable (o.ä.) zusammenkopiert werden müssen. Bei MySQL wird die gleiche Sicherheit nur durch die Verwendung der optionalen Platzhalter ("?") erreicht.

Die Benutzer-ID beider Varianten verdient eine gesonderte Betrachtung: Während MySQL eine fortlaufende Zahl beginnend bei 1 vergeben würde, wäre die MongoDB _id eine mehr oder weniger zufällig wirkende Kombination aus 24 hexadezimalen Zeichen. Die MySQL-Variante lässt sich vielleicht einfacher verarbeiten und ist auf jeden Fall kürzer, aber die Chancen, eine gültige ID zu erraten, tendieren bei MongoDB im Vergleich zu MySQL gegen Null.

 

Noch keine Kommentare. Schreib was dazu

Schreib was dazu

Die folgenden HTML-Tags sind erlaubt:<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>