MySQL Document Store

Zwischen den Welten: SQL oder NoSQL

mj sql nosql titelthema

NoSQL-Datenbanken setzen sich immer mehr durch. Gerade die Entwickler schätzen den  hohen Freiheitsgrad, der aus dem Verzicht auf starre Regeln entsteht, den jedoch traditionelle SQL-Datenbanken über Schemata und Tabellenstrukturen erzwingen. Aber warum sollte man sich zwischen den beiden Welten entscheiden müssen, wenn es doch Produkte gibt, die beide Welten verbinden? Dieser Artikel stellt den JSON Document Store der MySQL-Version 8.0 vor.

Sicht der Dinge …

Für den klassischen SQL-Anwender besteht die Welt nach wie vor aus Datenbanken (hier:company), Tabellen (employee_sql), Spalten (id, vorname,…) und Datensätzen.

mysql> select * from company.employee_sql;
+----+----------+------------+----------+
| id | vorname | nachname | firma |
+----+----------+------------+----------+
| 1 | Matthias | Jung | ORDIX AG |
| 2 | Theo | Test | ORDIX AG |
| 3 | Max | Mustermann | ORDIX AG |
+----+----------+------------+----------+

Beispiel eines klassischen relationalen Datenmodells am Beispiel der Tabelle employee_sql. Der NoSQL-Nutzer sieht zwar auch die Datenbank (company; hier als Objekt db instanziert). Die Tabelle, Spalten und Datensätze subsummiert er jedoch unter den Begrifflichkeiten Collection, Document und Field.


company JS> db.employee_json.find()
[
{
"_id": "00005c35f7f40000000000000001",
"department": 1,
"firma": "ORDIX AG",
"nachname": "Jung",
"vorname": "Matthias"
}, …

Dasselbe Datenmodell eines Mitarbeiters als JSON-Objekt.


Etwas unstrukturiert …

Bereits seit der Version 5.7 gibt es bei MySQL den  Datentypen JSON. Er erlaubt es, pro Zeile einer InnoDB Tabelle 1 GB an JSON-Informationen zu speichern. Der Datentyp prüft dabei beim INSERT der Zeile, dass der zu speichernde Inhalt validen JSON-Code repräsentiert und hilft so, Syntaxfehler zu vermeiden.


mysql> create table employee_json (nr int primary key, jdoc json ) engine = innodb;
mysql> insert into employee_json (nr, jdoc) values (1, ' {"vorname": "Matthias", "nachname":"Jung", "Firma": "ORDIX AG"} ');

So wurden in MySQL 5.7 JSON-Objekte in Tabellen ge­speichert.

Gerade etwas komplexere Strukturen mussten vom Entwickler auf diese Weise sehr sorgsam in die klassische SQL-Syntax integriert werden. Dies löste nicht immer nur Freude aus. Auch aus diesem Grund wurde in die aktuelle Version 8.0 ein neues Protokoll integriert, welches auch von der neuen MySQL Shell (in JavaScript, Python und SQL) umgesetzt wird: Das X-Protocol!


X für ein U …

Für den einen oder anderen altgedienten DBA (und der Autor zählt sich dazu) ist die neue Syntax zugegebener­weise etwas gewöhnungsbedürftig. Entwickler werden aber sicher ihre wahre Freude daran haben. Das oben – in klassischer SQL-Manier – implementierte Beispiel sieht unter Verwendung des X-Protokolls (im JavaScript-Modus) wie folgt aus:


mysqlsh root:root@localhost:3306/company>
company JS> db=session.getSchema('company')
company JS> db.createCollection('employee_json');
company JS> db.employee_json.add({
vorname: "Matthias",
nachname: "Jung",
firma: "ORDIX AG"});
company JS> db.employee_json.find()
[
{
"_id": "00005c35f7f40000000000000001",
"firma": "ORDIX AG",
"nachname": "Jung",
"vorname": "Matthias"
}
]
1 document in set (0.0039 sec) Zwischenüber­schrift


CRUDe Zeiten

Das Akronym CRUD steht für: Create, Read, Update und Delete. Damit sind die vier grundlegenden Opera­tionen des Datenmanagements gemeint. Das Erzeugen von Datensätzen erfolgt mit der Methode add():

 

db.employee_json.add({Vorname: "Max", Nachname: "Mustermann", Firma: "ORDIX AG"})
Das Lesen (Suchen) kann über die Methode find() gestartet werden:
db.employee_json.find("Vorname = 'Max'");
[
{
"Firma": "ORDIX AG",
"Nachname": "Mustermann",
"Vorname": "Max",
"_id": "00005c35f7f40000000000000008"
}
]

Zum Löschen und Ändern von Daten stehen modify() und remove() bereit:

db.employee_json.modify("id = '00005c35f7f40000000000000008'").set("Vorname", "Maximilian");
db.employee_json.remove("id = '00005c35f7f40000000000000008'")

Import- und Exportgeschäft

Auch der Import von bestehenden Daten, z.B. aus JSON-Dokumenten, ist einfach realisierbar. Dateien (auch gerne in einem komprimierten Format) können einfach über eine Import-Funktion der MySQL-Shell geladen werden:

mysqlsh root::64460/company --import employee_json.json.bz2 

Brücken bauen, Welten verbinden

Das Datenmodell unserer kleinen Testdatenbank besteht aus einer klassischen relationalen Tabelle (department) und einer Collection employee_json mit JSON-Dokumenten. In der Collection soll ein Feld mit einer Fremdschlüsselinformation aufgenommen werden, über die dann der Name der Abteilung aus der relationalen Tabelle dazu-gejoined werden kann. Dazu kann das Objekt einfach um ein Attribut erweitert werden. Eine Strukturänderung wie bei klassischen Tabellen (alter tableadd column…) ist nicht notwendig.

company JS> db.employee_json.modify('nachname = "Jung"').set("department", 1);

Zugegeben, die Syntax, um die Inhalte des DocumentStores mit relationalen Tabellen zu verknüpfen, ist etwas sperrig, mit ein bisschen Übung jedoch kein Problem:

mysql> select department, JSON_UNQUOTE(doc->"$.nachname") 
from department join employee_json
on JSON_UNQUOTE(doc->"$.department") = department.nr;
+------------+---------------------------------+
| department | JSON_UNQUOTE(doc->"$.nachname") |
+------------+---------------------------------+
| IT | Jung |
+------------+---------------------------------+

Generier Dir einen Index …

MySQL bietet keinen Weg an, ein JSON-Dokument attributspezifisch zu indizieren. Aber auch hier gibt es einen Ausweg. Seit MySQL 5.7 besteht die Möglichkeit, generierte (andere Hersteller nennen es „virtuelle“) Spalten zu erzeugen. Diese wiederum können mit einem Index belegt werden. Um in unserem Beispiel die Information (Attribut) „nachname“ zu verschlagworten, muss zunächst eine solche Spalte erstellt und anschließend indiziert werden.

mysql> alter table employee_json add nachname_json varchar(20) as (JSON_UNQUOTE(doc->"$.nachname"));
mysql> create index i_nachname on employee_json(nachname_json);

Bei der gezielten Suche nach einem Nachnamen innerhalb des JSON-Dokumentes sollte nun der Index verwendetet werden:

mysql> explain SELECT * FROM company.employee_json 
where JSON_UNQUOTE(doc->"$.nachname") = 'Jung';
********** 1. row **************
id: 1
select_type: SIMPLE
table: employee_json
partitions: NULL
type: ref
possible_keys: i_nachname
key: i_nachname
key_len: 83
ref: const
rows: 1
filtered: 100
Extra: NULL

Fazit

Mit den aktuellen Document-Store-Funktionalitäten bietet MySQL die Möglichkeit, die Konzepte von klassischen NoSQL-Datenbanken innerhalb bekannter Infrastrukturlösungen (MySQL) umzusetzen. Dabei lassen sich die beiden Welten „SQL“ und „NoSQL“ problemlos kombinieren und nebeneinander betreiben. Für den DBA hat dies den Vorteil, dass kein neues Produkt erlernt und in Betrieb gebracht werden muss. All die für MySQL bereits entwickelten Prozesse – vom Backup-, über das Monitoring bis hin zu Maßnahmen der Betriebsstabilität und -sicherheit (HA- & DR-Lösungen) – können einfach weitergeführt werden und müssen nicht für ein weiteres Datenbank­produkt erneut entwickelt werden. Selbst die altbekannten administrativen SQL-Kommandos können weiter genutzt werden, wenn der DBA nicht gewillt ist, sich der neuen JavaScript Syntax anzunehmen.

Der Entwickler freut sich über die Freiheiten, die ihm die JSON-Dokumente bei der Entwicklung bieten. Zudem muss er bei der Entwicklung nicht mehr in SQL-Kommandos denken, sondern kann objektorientiert mit seinen Daten arbeiten.

Matthias Jung ()