memcp: Eine OpenSource hochperformante Spaltenbasierte In-Memory-Datenbank als Alternative zu proprietären analytischen Datenbanken

In den letzten 13 Jahren gab es eine Menge Innovationen im Bereich spaltenbasierte Datenbanken. Zu verdanken ist das vor allem einem deutschen ERP-Hersteller, der sich entschieden hat, sich vom Datenbank-Marktführer abzusetzen und sein eigenes Ding zu machen. Es wurde zu einer Erfolgsstory. Zeit, dass OpenSource nachzieht.

In der kommerziellen Datenbank-Welt haben sich spaltenbasierte Datenbanken bereits längst durchgesetzt. Die OpenSource-Welt hinkt unterdes immer noch mit MariaDB und PostgreSQL hinterher. Das behindert nicht nur OpenSource-Projekte, sondern sämtliche Produkte, die auf OpenSource-Datenbanken setzen.

Aus diesem Grund stellen wir in diesem Artikel die memcp vor, eine spaltenbasierte In-Memory-Datenbank, die sowohl OLAP, als auch OLTP-Arbeitslasten performant handhaben kann.

Vorteile spaltenbasierter Datenbanken gegenüber Zeilenbasierter Speicher

Gerade komplexere Anwendungen wie z.B. ERP-Systeme haben oft Tabellen mit über hundert Spalten. Meistens steht in vielen Spalten nicht einmal ein Wert. Zeilenbasierte Datenbanken müssen aber für jede Speicherzelle entsprechend Speicher vorhalten.

Gleichzeitig gibt es auch viele Datenbank-Anfragen, die nur auf 2 Spalten arbeiten – meist einem Fremdschlüssel, nach dem gefiltert wird und einer Wert-Spalte, nach der summiert wird. Beispiel:

SELECT SUM(lineItem.quantity * lineItem.price) FROM lineItem WHERE lineItem.line = [ID]

In dieser SQL-Abfrage werden zum Beispiel von einer Liste von Verkäufen die Umsatzsumme gebildet – ein durchaus häufiger Anwendungsfall. Die Krux: Nutzt man eine Zeilenbasierte Datenbank wie MySQL, muss man immer die kompletten Datenbankzeilen von der Festplatte einlesen, um die zwei Spalten Menge und Preis miteinander zu multiplizieren. Die Spalten Einheit, Artikelnummer, Beschreibung, MwSt-Satz usw. hängen einfach am Datensatz „mit dran“.

Spaltenbasierte Speicher haben dieses Problem nicht. In einem spaltenbasierten Speicher werden alle Spalten separat voneinander im Speicher aufbewahrt. Das macht das Auslesen einer einzelnen „Komplett“-Zeile zwar etwas langsamer, die Gesamtperformance des Systems steigt aber im Verhältnis dazu stärker.

Spaltenbasierte Datenbanken erlauben auch Komprimierung und Optimierung

Während Datenbank-Zeilen sehr komplex aufgebaut sind – eine Datenbank-Zeile hat etliche Spalten und jede Spalte kann einen anderen Datentyp haben – sind spaltenbasierte Speicher einfacher aufgebaut: Man hat einfach eine riesige homogene Liste von Zahlen.

Das macht das Rechnen mit großen Datenmengen einfacher. So können zum Beispiel Zahlen „Bit-komprimert“ werden, das heißt: Enthält die Spalte nur Nullen und Einsen, braucht man keine 64 Bit, sondern nur ein einziges Bit, um die Zahl darzustellen. Eine Speicher-Ersparnis um das 64-fache!

Für Zeichenketten (Strings) gilt dasselbe: Sie können „Wörterbuch-komprimiert“ werden. Enthält die Spalte Geschlecht zum Beispiel nur abwechselnd die Werte „männlich“ und „weiblich“, speichern traditionelle Datenbanken die komplette Zeichenkette ab. Mit einer Wörterbuch-Komprimierung würde man jedem Wort eine Zahl zuweisen und könnte die Werte mit einem Speicherverbrauch von 1 Bit pro Wert abspeichern, anstatt mit 8 Bytes. Eine Ersparnis vom 256-fachen!

Die Balance zwischen OLAP und OLTP

Bei OLTP-Arbeitslasten haben zeilenbasierte Speicher hingegen weiterhin die Nase vorn. Eine typische OLTP-Anfrage wäre zum Beispiel das Anlegen eines neuen Datensatzes oder das verändern eines einzelnen Datums.

In einer zeilenbasierten Datenbank ändert man nur die eine Zeile – nutzt man hingegen einen bereits komprimierten Spalten-Speicher, kann es sein, dass man die komplette Komprimierung auflösen muss, den Speicher neu alloziieren und den kompletten Spaltenspeicher neu aufbauen muss.

Doch dafür gibt es auch eine Lösung: Man kombiniert einfach beide Welten.

Im sogenannten Stammspeicher (engl. „Main Storage„) werden die alten unveränderten Daten spaltenbasiert abgelegt. Der Stammspeicher ist performant für Statistiken einsetzbar und ändert sich nie. Im Stammspeicher werden auch Indizes gebildet, um Daten schneller suchen zu können.

Im Änderungsspeicher (engl. „Delta Storage„) hingegen speichert man die Lösch-Markierungen, sowie die zeilenbasierten neuen oder veränderten Datensätze. Das Einfügen in den Änderungsspeicher geht dabei sehr schnell, da man keine Komprimierung anwendet.

In einem gewissen Intervall „verfestigt“ man anschließend die Änderungen aus dem Änderungsspeicher, indem man einen neuen Stammspeicher aus dem alten Stammspeicher und dem Änderungsspeicher aufbaut. Somit hat man – wenn das Intervall 15 Minuten beträgt – spätestens nach 15 Minuten für alle neuen Daten die volle OLAP-Performance, während man trotzdem mit voller Performance OLTP-Arbeitslasten in den Änderungsspeicher schreiben kann.

OLAP-Lasten durch verteiltes Rechnen zähmen

Ein großer Nutzer von OLAP-Datenbanken sind sogenannte BI-Tools (Business Intelligence). Diese Programme erlauben das Analysieren und Durchforsten von Daten. Die Problematik hier: Je mehr Daten anfallen, desto langsamer wird die Analytik. Ein Klick im BI-Programm dauert dann mehrere Sekunden bis hin zu Minuten oder Stunden.

Allerdings ist hier Abhilfe möglich: Analytische Datenbank-Anfragen lassen sich sehr leicht parallelisieren. Anstatt dass eine CPU die Summe über alle Datensätze bildet, kann man den Speicher zum Beispiel auf 8 verschiedene Rechenknoten aufteilen. Das Kommando zum Errechnen der Summe verteilt man dann auf alle 8 Knoten und errechnet das Ergebnis dann aus den 8 Teilsummen.

Will man zum Beispiel, dass jeder Klick im BI-Tool nicht länger als 100ms Rechenzeit in Anspruch nimmt, kann man genau errechnen, wie groß das Verhältnis von RAM zu CPU-Kernen sein muss. So könnte man alle 1 GiB RAM dann einen CPU-Kern dazustellen.

In Produkten gesprochen, würde man beispielsweise einen Mini-Server aus einem Raspberry Pi mit 4 Kernen und 8 GiB RAM für die ersten 8 GiB Daten in der Datenbank verkaufen. Sobald der Nutzer mehr als 8 GiB Daten hat, kauft er einen zweiten Mini-Server und so weiter – damit steht für eine definierte Menge Speicher immer ausreichend Rechenzeit zur Verfügung, um den Speicher auch „verarbeiten“ zu können.

Datenbanken neu denken

Neben den Überlegungen zur bestmöglichen Speicherung, Komprimierung und Parallelisierung, soll auch die Kommunikation mit der Außenwelt etwas überdacht werden.

Die meisten Datenbanken haben ein SQL-Frontend, damit die Anwendung mit dem Storage kommunizieren kann. Es gibt aber auch andere Anfragesprachen, wie zum Beispiel SPARQL, einer Sprache, mit der man RDF-Daten aus dem semantischen Web abfragen kann. Mit dieser graphbasierten Anfragesprache ist es möglich, zum Beispiel Wiki-Daten abzufragen – beispielsweise die Namen aller Hauptstädte mit mehr als 1 Mio Einwohner, die sich in einem Land mit der Regierungsform Demokratie befinden. Auch diverse NoSQL-Datenbanken haben jeweils ihre komplett eigenen Anfragesprachen.

Momentan war es so, dass solche alternativen Datenbanken von Grund auf programmieren müsste, da es keine einheitliche Storage-Engine gibt. Mit memcp wird das anders:

memcp implementiert zuerst eine spaltenbasierte Storage-Engine, die man mit einer einfachen Schnittstelle auslesen und mit Daten befüllen kann. Gleichzeitig fungiert die memcp als HTTP-Server und nimmt Anfragen entgegen. Je nach Plugin kann man nun beliebige Endpunkte an dem HTTP-Server etablieren, die auf die Daten zugreifen können.

So könnte ein SQL-Endpunkt eingehende SQL-Anfragen in die interne Anfragesprache übersetzen und die Ergebnisse zeilenweise zurückgeben. Selbiges ist auch für die SPARQL-Anfragesprache möglich.

Gleichzeitig könnte man aber bereits komplette REST-APIs direkt in der Datenbank programmieren. Damit würde die Prozesstrennung zwischen Anwendung und Datenbank wegfallen, womit man nochmals eine deutliche Performance-Steigerung erreichen kann – gerade wenn die Datenbank-Daten sehr eng mit „spezielleren Algorithmen“ – z.B. einer KI – verknüpft sind.

Arbeitspferd ist hier die altbewährte funktionale Programmiersprache Scheme, ein LISP-Dialekt, der sich durch seine Einfachheit und Erweiterbarkeit auszeichnet. Wir haben in der memcp sogar eine Serialisierungsroutine für Scheme entwickelt, sodass man Rechenaufgaben mitten in der Berechnung abbrechen, übers Netzwerk versenden und auf dem nächsten Rechner weiterrechnen könnte.

Fazit

memcp wird auf Github unter https://github.com/launix-de/memcp abrufbar sein. Sollte es das noch nicht sein, bitten wir um Geduld, da wir erst eine ausreichende Demo-Bench bereitstellen wollen und grundlegende Design-Entscheidungen getroffen haben wollen, bevor wir beliebige OpenSource-Entwickler am Code mitarbeiten lassen.

memcp wird die Welt der In-Memory-Datenbanken zwar nicht revolutionieren – das hat schon das proprietäre Projekt – es wird aber die Technologie auch für freie Anwendungen öffnen und damit die Menschheit voran bringen. Und natürlich – (und das ist jetzt mein persönlicher Antrieb) – dem Hauptkonkurrenten der Launix seinen Wettbewerbsvorteil zu rauben.

de_DEGerman

Durch die weitere Nutzung der Seite stimmst du der Verwendung von Cookies zu. Weitere Informationen

Die Cookie-Einstellungen auf dieser Website sind auf "Cookies zulassen" eingestellt, um das beste Surferlebnis zu ermöglichen. Wenn du diese Website ohne Änderung der Cookie-Einstellungen verwendest oder auf "Akzeptieren" klickst, erklärst du sich damit einverstanden.

Schließen