© DrHitch/Shutterstock.com
Big Data

2 Datenbanken in moderner Cloud-Infrastruktur


2 Datenbanken in moderner Cloud-Infrastruktur

SQL Server sind in jeder IT-Umgebung und beinah in jeder zweiten Hosentasche zu finden. Obwohl SQL als Sprache seit 1992 kaum verändert wurde, gibt es heutzutage nur wenige Anwendungen, die sich bei der Datenverarbeitung nicht auf SQL stützen.

Big-Data-Umgebungen haben in Bezug auf die Datenverarbeitung viele Besonderheiten, wenn man sie z. B. mit einem klassischen CMS vergleicht. Hier sind die wichtigsten:

  • Der Datenbankserver ist immer von dem Application Server getrennt, befindet sich nicht auf der gleichen Maschine und manchmal nicht einmal auf dem gleichen Kontinent.
  • Es werden Read-only-Kopien von der gleichen Datenbank betrieben. Lese- und Schreibzugriffe müssen oft getrennt programmiert werden.
  • Großflächige Locks (wie Tablelocks) betreffen tausende von Nutzern.
  • Mehrere Entwicklerteams arbeiten zusammen. Oft haben die Frontend-Entwickler keinen direkten Kontakt mit dem Datenverarbeitungsteam.

Im Folgenden sollen einige Empfehlungen und Tricks gezeigt werden.

Wenige Requests

Wie alle Anfragen übers Netz ist die Kommunikation mit der Datenbank mit Latenzen verbunden. Bei einer Cloud-Anwendung lässt es sich von Anfang an nicht immer vorhersagen, wie weit die Datenbank vom Application Server entfernt wird. Bei einer lokal installierten Datenbank sind die Mehrkosten für eine zusätzliche SQL-Anfrage vernachlässigbar. Sie liegen deutlich unter einer Millisekunde. Eine Datenbank im gleichen Netz kostet um die zwei Millisekunden pro Abfrage. Sollte es dazu kommen, dass der Server in einem anderen Rechenzentrum steht, steigern sich die Kosten um mehr als das Zehnfache.

Bei einer verteilten Anwendung sollte man immer davon ausgehen, dass sich eine Datenbank über die Zeit in einem anderen Rechenzentrum befinden kann. Wenn man das nicht tut, kann es dazu kommen, dass bei einem Ausfall oder Update einer Komponente gleich das ganze Rechenzentrum aus dem Betrieb genommen werden muss.

Oft ist es günstiger, mehr Daten zu laden, als eine zusätzliche Anfrage zu starten. Sollten z. B. für einen gegebenen Abteilungsleiter der Vorgesetzte und alle Mitarbeiter geladen werden, ist es sinnvoll, die ganze Belegschaft der Firma zu laden und die Zuständigkeiten zwischen Kollegen in der Businessschicht lösen. So ähnlich funktioniert ein MapReduce-Verfahren [1]. Es wurden drei Tests durchgeführt, bei denen die Daten von drei MySQL-Servern gelesen wurden:

  • Lokal installierter Server
  • Server im gleichen lokalen Netz
  • Server im AWS-Rechenzentrum in Irland

Ich konzentriere mich auf zwei Diagramme. In Abbildung 2.1 sieht man, dass es sich grob um Faktor 100 handelt, wenn man die Konfigurationen 1 und 3 vergleicht.

alukhanov_1.png

Abbildung 2.1: Eine Anfrage an MySQL-Server in unterschiedlichen Locations

Beim zweiten Vergleich wurde errechnet, wann es sich lohnt, die Daten in größeren Mengen zu laden, um die Anzahl von Requests zu reduzieren. Es wurden in allen drei Konfigurationen zwei Use Cases verglichen:

  • drei nacheinander durchgeführte JDBC-Requests
  • eine Anfrage, die das Zehnfache von Daten lädt

Die Ergebnisse sind in den Abbildungen 2.2 bis 2.4 präsentiert. In allen drei Umgebungen bleibt das Laden des Zehnfachen an Daten bis zu einer Datenmenge von etwa 10 KB bis 50 KB an Nettodaten (0,1 MB bis 0,5 MB „brutto“) günstiger, als diese Daten in drei Requests abzuholen.

alukhanov_2.png

Abbildung 2.2: Mehrere Requests vs. zu viele Daten; lokale Installation

alukhanov_3.png

Abbildung 2.3: Mehrere Requests vs. zu viele Daten; gleiches LAN

alukhanov_4.png

Abbildung 2.4: Mehrere Requests vs. zu viele Daten; entferntes Rechenzentrum

Besonders im Team lässt sich nicht immer kontrollieren, ob die Kommunikation mit dem Datenserver optimal verläuft. Als Abhilfe empfehle ich, die Datenzugangsfunktionen möglichst „gierig“ zu entwickeln. Beispielsweise kann man auf DAO-Ebene gar keine Funktionen anbieten, die einzelne Elemente laden. Statt public Customer loadCustomer(String customerCode) muss man nur die Methode public Set<Customer> loadCustomers(Set<String> customerCodes) zu Verfügung stellen. Es wird indirekt den Kollegen anleiten, das Laden von mehreren Objekten außerhalb der Verarbeitungsschleife zu platzieren.

Zuletzt ist anzumerken, dass alle Messungen mit deaktivierter Komprimierung erfolgten. Überraschenderweise wies ein Test mit MySQL mit Komprimierung, bei dem die Datenmenge um etwa 50 Prozent reduziert wurde, eine dramatische Verschlechterung auf. Nur mit der Kommunikation mit dem AWS-Datenzentrum kam ich auf ungefähr die gleiche Performance. In zwei anderen Umgebungen waren alle Tests etwa doppelt so langsam, wenn man die Komprimierung aktiviert. Es ist selbstverständlich keine Regel. Es zeigt nur, dass die Optimierungen, die über eine für diese Umgebung übliche Konfiguration hinausgehen, unbedingt hinterfragt werden müssen.

Paralleles Laden

Eine Alternative zum Reduzieren der Anzahl der Requests sind die parallelen Anfragen. Diese sind durchaus sinnvoll, aber mit Problemen verbunden. Je nach Architektur können sie entscheidend sein.

Die JDBC-Infrastruktur ist nicht für Multi-Threading ausgelegt. Für mehrere parallele Anfragen werden mehrere Connections benötigt. Kapazitäten von Connection Pool und Lizenzbeschränkungen des Servers limitieren die Anzahl gleichzeitiger Verbindungen. Es ist daher durchaus denkbar, in zwei oder drei Threads zu laden, das macht allerdings das Laden mit n Threads nicht möglich.

Zusätzliche Einschränkung macht ein Object-Relational-Mapping-Framework (kurz ORM), wie z. B. Hibernate [2]. Es ist kein Problem, mehrere Sessions zu öffnen oder sie aus dem Pool zu holen. Das Problem ist der First-Level-Cache des JPA. Die Objekte, die mit der Sessionnummer 1 geladen werden, kann man nicht mit der Sessionnummer 2 speichern. Alles in allem würde ich auf das parallele Laden beim Einsatz eines ORM-Frameworks ganz verzichten.

Cache vermeiden

Die Tatsache, dass zwischen der Datenschicht und dem Verbraucher mehrere Schichten und oft mehrere physikalische Server liegen, macht die Nutzung eines Second-Level-Caches oft aufwändig und teuer. Man muss es vermeiden, den Cache gleich bei der Planung in die Performanceberechnung einzubeziehen. Ein klassischer Cache ist gedacht, um die Datenbank zu entlassen, nicht aber, um die Zugriffszeit zu verbessern. Bei den richtig gesetzten Anforderungen bemisst sich die Zugriffszeit nach dem Maximalwert oder nach Quantil 99 [3]. Das heißt, dass nur die langsamsten 1 Prozent der Anfragen eine Rolle spielen. Der Durchschnittswert ist egal.

Egal wie gut ein Cache ist, er deckt nie 100 Prozent aller Requests ab. Das bedeutet, dass die wenigen verbliebenen Anfragen an die Datenbank so verlaufen werden, als ob kein Cache existiert.

Eine ganz andere Geschichte ist ein Zwischenpuffer mit 100 Prozent Abdeckung. Eine solche Lösung verbessert zwar die für die Abgabe relevanten Performancewerte, geht aber kostenmäßig weit über einen klassischen Cache hinaus. Im Falle einer verteilten Umgebung benötigt eine solche Architektur einen zusätzlichen Cluster mit Maschinen und viel Arbeitsspeicher. In vielen Fällen ist ein besserer Datenbankserver viel günstiger.

Keine Auto-Increment-Felder

Eine weitere Besonderheit eines Big-Data-Systems ist, dass die Daten nicht in der Form gespeichert werden, in der sie verarbeitet und angezeigt werden. Zwischen Persistenzschicht und Anzeige liegen mehrere Aggregationsschnittstellen. Das bedeutet unter anderem, dass alle DB-relevanten Artefakte nicht an öffentliche APIs weitergereicht werden können. Ein technischer Primärschlüssel (auch „surrogate key“ genannt) kann für die Adressierung z. B. deswegen nicht benutzt werden, weil das End-Objekt aus mehreren Datenbanken geladen wird und in der Persistenzschicht mehrere Primärschlüssel besitzt.

Wenn die Nutzung eines Autoincrement-Felds in der Präsentationsschicht nicht möglich ist, wird man mehrere zusätzliche Aufrufe brauchen, um die technischen Primärschlüssel aufzulösen.

Wenn das Auto-Increment-Feld in die Präsentationsschicht weitergereicht wird, bildet ein solcher Schlüssel eine Lücke, was ermöglicht, direkt auf die Datensätze zuzugreifen, die in einem anderen Berechtigungsbereich liegen. Das...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Teams

Für Firmen haben wir individuelle Teamlizenzen. Wir erstellen Ihnen gerne ein passendes Angebot.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang