Mnesia
# Mnesia

Mnesia ist ein in Erlang geschriebenes Datenbanksystem. Es wird ausschließlich im direkten Verbund mit Erlang benutzt, eine Anbindung an andere Sprachen existiert nicht. Mnesia besitzt weiche Echtzeit-Fähigkeiten, kann leicht verteilt konfiguriert werden und ist außerdem auf Geschwindigkeit optimiert.

## Name

Joe Armstrong, einer der Hauptentwickler von Erlang, beantwortet die Frage nach dem Namen Mnesia in seinem Buch Programming Erlang folgendermaßen:

   „The original name was Amnesia. One of our bosses didn’t like the name. He said, ‘You can’t possibly call it Amnesia–you can’t have a database that forgets things!’. So we dropped the A and the name stuck.“
   „Der ursprüngliche Name lautete Amnesia. Aber einer unserer Chefs mochte diesen Namen nicht. Er meinte: ‚Ihr könnt das Ding doch nicht Amnesia nennen – ihr könnt keine Datenbank entwickeln, die Sachen vergisst!‘. Also haben wir das A weggelassen und der Name blieb.“

## Einsatz

Wie schon Erlang wurde Mnesia für den Einsatz in Telekom-Umgebungen entwickelt, in denen es auf niedrige Latenz und sehr hohe Verfügbarkeit sowie Parallelität ankommt. Mnesia wurde nicht entworfen, um herkömmliche, SQL-basierte Datenbanken zu ersetzen, sondern eher als in die Sprache eingebettete Datenbank, ähnlich wie die Berkeley DB.

## Integration in Erlang

Mnesia wurde stark auf Erlang spezialisiert, so unterstützt es das Speichern jeglicher Erlang-Terme. Erlang-Terme sind hierbei die in der Sprache selbst verwendete Datenstrukturen-/Typen:

* Listen: [1,2,3]
* Skalare: 42
* Atome: helloworld, xyz
* Tupel: {hello,1,3.4,[1,2,3],{5,abc}}, Records: R = #recordname{a=1,b=2}
* Funs: fun -> doSomething end.

Es entfällt also die Umwandlung von Datentypen.

Eine Verwendung von anderen Sprachen als Erlang ist nicht möglich. Innerhalb von Erlang wird das Modul mnesia genutzt, um mit dem Datenbankserver zu kommunizieren, der mit diversen Erlang-Prozessen arbeitet.

## Speichermodell

Mnesia speichert Erlang-Tupel in Tabellen. Die Tupel sind nach folgendem Muster aufgebaut:

Tabellenname dient Mnesia dazu, den Datensatz in die richtige Tabelle zu stecken, während Key ein in der jeweiligen Tabelle eindeutiger Schlüssel ist (typisch für relationale Datenbanksysteme), zum Beispiel eine fortlaufende Nummer. Der Rest des Tupels sind andere Datenfelder.

Aufgrund dieser Struktur werden zumeist Records für die Arbeit mit Mnesia genutzt. Der Record -record(table1,{field1,field2=defaultvalue}). wird in ein Tupel {table1,Wert1,Wert2} umgewandelt, und ist damit gut geeignet in Mnesia gespeichert zu werden.

## Transaktionen

Das Lesen, Schreiben und Abfragen sollte nur in sogenannten Transaktionen (transactions) stattfinden. Transaktionen sind Erlang-Funs (Fun ist in Erlang synonym mit anonymer Funktion), in denen die Mnesia-Befehle für Lesen, Schreiben und Abfragen ausgeführt werden. Eine solche Fun wird dann dem Mnesia-Transaktionsmanager übergeben.

Der Sinn von Transaktionen besteht darin, Atomarität (Unteilbarkeit) und dadurch Konsistenz zu erreichen. Entweder die gesamte Transaktion hat Erfolg, oder die gesamte Transaktion schlägt fehl. Die Datenbank bleibt immer konsistent. Beispiel: Zwei zusammenhängende Datensätze sollen in die Datenbank geschrieben werden. Das Schreiben des ersten Datensatzes ist erfolgreich, das des zweiten schlägt fehl (zum Beispiel wegen eines vergessenen Feldes im einzufügenden Datensatz). Nun wird der erste Schreibvorgang wieder rückgängig gemacht, um wieder Konsistenz zu erreichen. Danach wird das Programm darüber informiert, dass etwas schiefgelaufen ist.

Neben Transaktionen gibt es dirty operations, also schmutzige Operationen, die um ein Vielfaches schneller sind als Transaktionen, dafür aber keinerlei Garantie für Konsistenz oder Atomarität geben.

## Die Arbeit mit Mnesia – einige kleine Beispiele

### Schema

Bevor die Datenbank genutzt werden kann, muss ein sogenanntes Schema erstellt werden. Ein Schema ist eigentlich nur ein Ordner im aktuellen Verzeichnis, das nach dem Muster Mnesia.node benannt ist, bei einer unbenannten Erlang-Instanz also Mnesia.nonode@nohost. Das Schema wird genutzt, um je nach Modus Daten auf der Festplatte zu sichern und die Verlinkung mit anderen Mnesia-Instanzen zu verwalten.

Dieser Befehl erstellt für die beiden genannten Knoten (Erlang-VMs, die in der Lage sind, über das Netzwerk angesprochen zu werden) sowie den aktuellen Knoten (node) die notwendigen Dateien. table1 ist zurzeit die einzige Tabelle in der Datenbank. Die gesamte Struktur nimmt bei 20 Einträgen etwa 28 kB ein, eine leere Datenbank benötigt etwa 20 kB.

Das Schema speichert also die Metainformationen für die Arbeit mit einer Datenbank. Das Verknüpfen mit einem laufenden Mnesia-Server geschieht automatisch, d. h. wenn ein Mnesia-Server in einem Node gestartet wird und er ein Schema mit zum Node-Namen passendem Namen findet, wird dieses genutzt.

Nachdem ein Schema angelegt wurde, muss Mnesia auf allen beteiligten Nodes gestartet werden:

### Tabellen

Ein wichtiges Grundelement von relationalen Datenbanken sind Tabellen. Eine Tabelle besteht in Mnesia aus einer Reihe von Erlang-Tupeln, die alle mit demselben Namen beginnen, nämlich dem der Tabelle. Am besten hierfür geeignet ist ein Erlang-Record:

table1 ist der Name der Tabelle, der mit dem Record-Namen übereinstimmen muss. Das zweite Argument ist eine Liste von Optionstupeln. Hier verwendet:

* {attributes,[field1,field2]} – Liste der Felder. record_info gibt die Liste der Felder des gegebenen Records zurück.
* {disc_copies,['node1@host.example.org','node2@host.example.org']} – Speichermethode. Neben disc_copies (Ort: RAM; Sicherung auf Festplatte) existieren nur-RAM (ram_copies bzw. kein Argument) und nur-Festplatte (disc_only_copies). Die Nodeliste enthält dabei immer nur Nodes, die beim Erzeugen des Schemas angegeben wurden, da ein spezielles Cookie zwischen den Schemas übereinstimmen muss. Diese Nodeliste konfiguriert die Tabelle so, dass Kopien der Tabelle auf zwei Nodes abgelegt werden, aber nicht auf dem Haupt-Node, auf dem die Anwendung läuft, die diese Funktion gerade ausführt. Ein typisches Szenario wäre eine Frontend-Backend-Anwendung, bei der der Anwendungsserver vor allem Rechenleistung besitzt und die beiden anderen Nodes den Speicherplatz bereitstellen.

### Schreiben

Zunächst wird ein Funktionsobjekt mit dem gewünschten Verhalten erzeugt und dann dem Transaktionsmanager übergeben. Die in Transaktionen verwendeten Mnesia-Funktionen laufen nur im Transaktionskontext, d. h. es ist mit diesen Funktionen nicht möglich, außerhalb einer Transaktion die Datenbank zu ändern.

### Abfragen

Das Abfragen geschieht mittels der in Erlang oft eingesetzten Listenkomprehensionen. Um alle Werte von field1 in Datensätzen bei denen field2 größer als 100 ist zu extrahieren, wird folgende Abfrage formuliert:

QLC ist hierbei ein Modul zur Abfrage von verschiedenen Tabellen, zum Beispiel ETS oder eben Mnesia. qlc:q kompiliert eine Listenkomprehension, qlc:e führt sie aus und gibt die Werte zurück. Mit diesen Listenkomprehensionen sind auch Joins möglich.

Das SQL-Äquivalent für diese Abfrage würde folgendermaßen lauten:

