Home ORDIX AG             Dienstleistung             Trainingsshop    Kunden / Referenzen Aktuelles    Kontakt
Home  Pfeil  ORDIX News  Pfeil  3/2008  Pfeil  Java/J2EE/JEE
suche: 
Dieser Artikel richtet sich an Softwareentwickler- und architekten, die Hibernate performant einsetzen möchten.

Glossar

Cache
Ein Cache erhöht die Leistung einer Anwendung, indem wiederholte Zugriffe auf Datenstrukturen durch Zwischenspeicherung im Hauptspeicher beschleunigt werden.
Timestamp
Ein Zeitstempel, der den Zeitpunkt der letzten Bearbeitung von Datensätzen speichert.

Performance-Tests mit Hibernate (Teil I)

First-/Second Level und Query Cache - schneller als die Polizei erlaubt ...

Abb. 1: Klassendiagramm.
Szenario First Level Cache Szenario Second Level Cache Szenario Query Cache
Abfrage Selektiere alle Studenten mit dem Namen "Larry"
Iteration der Abfragen 100 100 (2 Threads, die jeweils nacheinander 50 Abfragen ausführen) 100 (einmal mit und ohne Query Cache)
Anzahl der gefundenen Studenten in der DB 10.000
Aktive Caches First Level Cache First Level Cache, Second Level Cache First Level Cache, Second Level Cache
Cache Mode Nicht vorhanden Read Only
Cache Provider Nicht vorhanden EHCache
Abb. 2: Übersicht über die drei Testszenarien.
Abb. 3: Messergebnisse des First Level Cache.
Abb. 4: Messergebnisse des First- und Second Level Cache.
Abb. 5: Messergebnisse des Query Cache.

Das Framework Hibernate ist inzwischen sehr weit verbreitet. Ein Grund dafür ist sicherlich die gute Performance von Hibernate. Aus diesem Anlass starten wir eine Artikelreihe über Performance-Tests mit Hibernate. In dieser Ausgabe beleuchten wir die Auswirkungen des First- und Second Level Cache und des Query Cache.

Vorwort

In der ORDIX News 3/2006 haben wir bereits über die Grundlagen des Caching mit Hibernate [1] berichtet. Es ist sinnvoll, sich diese Grundlagen zu verinnerlichen, bevor Sie sich mit diesem Artikel auseinandersetzen.

Vorbereitung

Für unsere Performance-Tests wurde ein Klassenmodell entworfen, das eine Vorlesung mit Studenten und Dozenten in Kombination mit einer Bibliothek modelliert (siehe Abbildung 1). Das objektrelationale Mapping wurde mit Annotations realisiert. In diesem Artikel beschränken wir uns jedoch auf die Klasse Student.

Weiterhin wurden verschiedene Datenbank-Schemata mit einer unterschiedlichen Anzahl an Datensätzen angelegt, um bei den Tests flexibel zu sein.

Zur Messung der Ergebnisse wurde mit Hilfe von Java eine Stoppuhr entwickelt, die neben dem Starten und Stoppen der Zeit in Millisekunden auch den Durchschnittswert ermittelt. Messungen starten und enden mit einer Transaktion. Dazwischen finden die Testabläufe statt. Der Aufbau der Verbindung zur Datenbank fließt dabei nicht in die Testergebnisse mit ein.

Die Performance-Tests wurden auf einem Intel® Core™ 2 CPU 4300 @ 1.80 GHz mit 1 GB RAM durchgeführt. Als Betriebssystem wurde Microsoft Windows XP Professional (Version 2002) mit Service Pack 2 verwendet. Eclipse 3.2 mit dem JDK 1.6 Update 4 diente als Entwicklungsumgebung.

Mit drei verschiedenen Testszenarien (siehe Abbildung 2) untersuchen wir im Folgenden anhand der eben vorgestellten Umgebung die Auswirkungen des First- und Second Level Cache und des Query Cache.

First Level Cache - kurzlebig aber effektiv

Der First Level Cache ist standardmäßig aktiviert und kann nicht ausgeschaltet werden. Er ist mehrfach vorhanden und wird für die Dauer der Session von der aktuellen Hibernate Session selbst implementiert. Im ersten Szenario (siehe Abbildung 2) geht es um die Analyse der Geschwindigkeit des First Level Cache und die Vorteile, die sich aus der Nutzung ergeben.

Die Statistik zeigt, dass die erste Abfrage (siehe Abbildung 3) mit 1453 ms fast doppelt so viel Zeit benötigt wie die zweite Abfrage (734 ms). Die weiteren Messergebnisse schwanken zunächst noch geringfügig und pendeln sich dann im Bereich zwischen 600 ms und 670 ms ein.

First Level Cache - Der Standard steigert Performance

Die Messergebnisse lassen sich folgendermaßen erklären: Zunächst öffnet die Session-Factory eine Session mit der Datenbank. Das hat die Aktivierung des First Level Cache zur Folge.

Danach prüft die erste Abfrage, ob die Objekte im Cache vorhanden sind. Das ist nicht der Fall, weil der Cache leer ist. Anschließend findet ein Zugriff auf die Datenbank statt. Dabei übermittelt Hibernate das generierte Select-Statement an die Datenbank, speichert die zurückgelieferten Daten in Objekten und lädt diese in den dazugehörigen Cache-Bereich.

Die zweite Abfrage prüft erneut, ob die Objekte im Cache vorhanden sind. Zu diesem Zeitpunkt kann Hibernate die Daten direkt aus dem First Level Cache beziehen, weil die erste Abfrage die benötigten Daten bereits im Cache abgelegt hat. Die weiteren Abfragen verfolgen die gleiche Strategie.

Der Einsatz des First Level Cache führt demzufolge zu Performance-Steigerungen, da durch den Zugriff auf das schnellere Speichermedium deutlich schnellere Antwortzeiten erzielt werden können. Zudem wird die Datenbanklast reduziert, da Hibernate nicht mehr bei jedem Laden eines Objekts die Informationen aus der Datenbank beziehen muss.

Second Level Cache - ein langes Leben

Im Gegensatz zum First Level Cache ist der Second Level Cache optional zuschaltbar und nur einmal pro Applikation vorhanden. Des Weiteren ist er langlebiger, weil er für die Lebensdauer einer SessionFactory existiert. Das zweite Szenario (siehe Abbildung 2) untersucht die Performance-Steigerung durch den Einsatz des Second Level Cache, die erst bei mehreren Threads zum Tragen kommt.

Abbildung 4 zeigt die Messergebnisse der beiden durchgeführten Threads. Es ist deutlich zu erkennen, dass beim ersten Thread die erste Abfrage mehr Zeit benötigt, als die darauf folgenden. Die Messungen pendeln sich anschließend zwischen 600 ms und 800 ms ein. Der zweite Thread bewegt sich ebenfalls in diesem Bereich.

Second Level Cache für gleiche Abfragen innerhalb einer SessionFactory

Der erste Thread baut mit Hilfe der Session-Factory eine Session mit der Datenbank auf, die eine Aktivierung des First- und des Second Level Cache zur Folge hat. Es findet die erste Abfrage statt. Die Daten werden in Objekte gespeichert und anschließend in den First- und den Second Level Cache geladen. Die darauf folgenden Abfragen des ersten Threads laden die Objekte aus dem First Level Cache. Dadurch benötigt die erste Abfrage mehr Zeit als die zweite.

Nachdem der erste Thread beendet ist, startet der zweite Thread. Dieser baut ebenfalls mit Hilfe der SessionFactory eine neue Session auf und aktiviert dadurch die beiden Caches. Der First Level Cache ist bei der ersten Abfrage leer, da dieser nur für die Lebensdauer einer Session existiert. Der Second Level Cache existiert hingegen für die Lebensdauer einer SessionFactory und wurde bereits zuvor durch den ersten Thread mit Objekten befüllt.

Startet der zweite Thread die erste Abfrage, wird zunächst erfolglos der First Level Cache durchsucht. Anschließend prüft Hibernate den Second Level Cache. Dieser beinhaltet die gesuchten Objekte, weil es sich bei den beiden Threads um die gleiche Abfrage handelt. Aus diesem Grund benötigt die erste Abfrage des zweiten Threads deutlich weniger Zeit als die erste Abfrage des ersten Threads.

Der Second Level Cache wird insbesondere auf Ebene einer Virtual Machine oder eines Clusters eingesetzt. Bei der ersten Möglichkeit werden alle Objekte, die in einer Virtual Machine gespeichert oder geladen werden gecacht. Wenn z. B. mehrere Benutzer den gleichen Buchtitel abfragen, muss der Buchtitel nicht für jeden Benutzer neu geladen werden, sondern kann direkt aus dem Second Level Cache bezogen werden. Bei einem Cluster werden ebenfalls alle Objekte geladen, auch wenn die Applikation über mehrere Rechner verteilt ist.

Es gibt aber auch Situationen, in denen der Einsatz des Second Level Cache nicht empfehlenswert ist, z. B. bei einer großen Einfüge-operation. In diesem Fall ist es ratsam, den Second Level Cache zu deaktivieren, weil sonst unnötiger Overhead durch Speichern der Objekte in den Second Level Cache entsteht.

Query Cache - bei wiederholten Abfragen

Der Query Cache ist ebenfalls optional zuschaltbar und ist bei Abfragen sinnvoll, die sich oft wiederholen. Neben SQL-Anweisungen (einschließlich aller eingebundenen Parameter) enthält der Query Cache auch die Identifikatoren der Ergebnismenge. Mit dem Query Cache ist auch immer ein Zeitstempel-Cache aktiv, der prüft, ob die Ergebnismenge der Abfrage noch aktuell ist. Es stellt sich nun die Frage, wie viel Performance-Gewinn der Query Cache bei gleichen Abfragen bringt. Um diese Frage zu beantworten, testen wir nun das dritte Szenario.

Abbildung 5 zeigt, dass durch die Aktivierung des Query Cache die Abfragen wesentlich schneller erfolgen. Im Durchschnitt benötigen die Abfragen ohne Query Cache 688 ms. Aktiviert man den Query Cache, ist eine deutliche Leistungssteigerung zu erkennen. Unter Verwendung des Query Cache benötigen die Abfragen durchschnittlich 545 ms. Das entspricht einem Performance-Vorteil von circa 20 Prozent.

Query Cache - großer Erfolg bei sinnvollem Einsatz

Dieser Effekt hat folgenden Hintergrund:

Nach der ersten Abfrage werden alle drei Caches gefüllt. Der First- und der Second Level Cache speichern die Objekte, während der Query Cache das SQL-Statement und die Identifikatoren der Ergebnismenge speichert. Weiterhin wird ein Zeitstempel gesetzt.

Die zweite Abfrage prüft, ob der Query Cache das SQL-Statement beinhaltet. Unter Verwendung der gleichen Abfrage ist die Suche erfolgreich. Anschließend findet eine Prüfung des Zeitstempels statt. Da keine Änderungen in der Tabelle durchgeführt wurden, ist der Zeitstempel aktuell und die Ergebnismenge wird nicht verworfen.

Im nächsten Schritt sendet Hibernate die Identifikatoren der Ergebnismenge an den Second Level Cache und durchsucht diesen. Der Second Level Cache liefert anschließend die Objekte zurück und die Session speichert diese zum Beispiel in einer Liste.

Ein Datenbankzugriff wird dadurch vollständig vermieden, was - ähnlich wie beim First- und Second Level Cache - zu Performance- Steigerungen und einer Datenbankentlastung führt. Der Query Cache bietet sich nicht an, wenn die Tabelle oft modifiziert wird, weil dann der Zeitstempel häufig nicht mehr aktuell ist und die Ergebnismenge verworfen und neu geladen werden muss.

Fazit

Das Cache-System bietet effiziente Möglichkeiten, um Performance-Optimierungen zu erzielen. Bei falschem Einsatz kann das Caching aber auch das Gegenteil bewirken. Als Beispiel sei hier das Modifizieren von Tabellen unter Verwendung des Query Cache genannt. Im nächsten Teil der Reihe werden die verschiedenen Caching-Strategien des EHCache näher unter die Lupe genommen und bewertet.

ORDIX verfügt in diesem Bereich über Erfahrung aus vielen Projekten. Gerne geben wir diese an Sie weiter [2]. Besuchen Sie unseren Trainingsshop oder sprechen Sie uns an!

Alexander Keil (info@ordix.de).