Daily CheckIn

This commit is contained in:
marcodn 2024-09-26 23:43:15 +02:00
parent f2f1122846
commit ebec44cdee
15 changed files with 276 additions and 260 deletions

View file

@ -4,6 +4,7 @@
"autovacuum",
"denormalisierte",
"Denormalisierung",
"deserialisiert",
"editionswissenschaftlich",
"EFFW",
"Ehcache",
@ -57,6 +58,8 @@
"adressee",
"ajax",
"american",
"appuser",
"appuserrole",
"asadmin",
"authorperson",
"birthstartday",
@ -80,6 +83,7 @@
"endday",
"endmonth",
"endyear",
"extendedbiography",
"ferniunithesis",
"firstname",
"fmtutil",
@ -105,6 +109,7 @@
"searchdocument",
"searchfulltext",
"searchreference",
"seqscan",
"servlet",
"servlets",
"sitecity",

View file

@ -73,7 +73,7 @@ zu verringern. Um Anfragen die den Zugriff auf die Festplatte benötigen effizie
every fit/.style={inner sep=1em,draw}
]
%https://docs.oracle.com/javaee/6/tutorial/doc/bnacj.html
\node (browser) [block] {WebBrowser};
\node (browser) [block] {Webbrowser};
\node (fitClient) [fit=(browser)] {};
\node [left] at (fitClient.west) {Client};
@ -113,13 +113,13 @@ von Datenbankverbindungen zu definieren \citep[68]{MüllerWehr2012}. Dadurch kan
gehalten werden als die Anzahl der Benutzer die an der Anwendung arbeiten. Zusätzlich werden die Transaktionen über
\textit{Stateful Session-Bean (SFSB)} gehandhabt, welche automatisch vor dem Aufruf erzeugt und danach wieder gelöscht
werden. Dies birgt allerdings den Nachteil, dass der \textit{Persistenzkontext} sehr groß werden kann, wenn viele
Entities in den \textit{Persistenzkontext} geladen werden. Da dies häufig zu Speicher- und damit Performanz-Problemen
Entities in den \textit{Persistenzkontext} geladen werden. Da dies häufig zu Speicher"= und damit Performance"=Problemen
\citep[79]{MüllerWehr2012} führen kann, muss hier darauf geachtet werden, nicht mehr benötigte Entities aus dem
\textit{Persistenzkontext} zu lösen.
\subsection{Glassfish - Java Persinstance API}
Die \textit{Java Persistence API (JPA)} wird als First-Level-Cache in Java-EE-An\-wen\-dung verwendet, hier nehmen die
Die \textit{\ac{JPA}Java Persistence API (JPA)} wird als First-Level-Cache in Java-EE-An\-wen\-dung verwendet, hier nehmen die
Objekte einen von vier Zuständen ein \citep[57]{MüllerWehr2012}. Im Zustand \textit{Transient} sind die Objekt erzeugt,
aber noch nicht in den Cache überführt worden. Wenn diese in den Cache überführt worden sind, nehmen sie den Zustand
\textit{Verwaltet} ein. Ist das Objekt aus dem Cache und der Datenbank entfernt worden, nimmt es den Zustand

View file

@ -12,7 +12,7 @@ Um die Messungen etwas zu vereinfachen wurde ein Skript erstellt um die Aufrufe
Messungen durchzuführen werden die Befehl, wie in \autoref{lst:calling_script_exec} dargestellt aufgerufen.
Durch die nummerierten Präfixe können im Nachgang über die \textit{pgBadger}"=Berichte die \ac{SQL}"=Abfragen verglichen
werden. Wichtig hierbei ist noch, dass vor dem \textit{measrun}-Aufruf überprüft wird, ob die Docker-Container
gestartet und initialisiert sind. Wenn dies nicht der Fall ist, laufen die Abfragen ins leere. Am einfachsten ist das,
gestartet und initialisiert sind. Wenn dies nicht der Fall ist, laufen die Abfragen ins Leere. Am einfachsten ist das,
wie dargestellt, über die Statistik von Docker zu ermitteln. Darüber wird überwacht, das die CPU-Auslastung auf ein
niedriges Level fällt, danach kann das Skript für die Messungen gerufen werden.

View file

@ -8,10 +8,10 @@
\chapter{JSF Performance Measure}
\label{ap:jsf_performance_measure}
Für die Protokollierung der Abläufe im \ac{JSF} werden zwei Klassen benötigt. Die Factory \ref{lst:logger_factory},
Für die Protokollierung der Abläufe im \ac{JSF} werden zwei Klassen benötigt. Über die Factory \ref{lst:logger_factory},
wird die Wrapper"=Klasse in die Bearbeitungsschicht eingeschleust. Diese Wrapper"=Klasse \ref{lst:logger} beinhaltet
dann die eigentliche Performance-Messung, inklusive der Ausgabe in die Log"=Datei des \textit{Glassfish}"=Servers.
Zusätzlich muss in der Konfiguration \textbf{faces-config.xml} noch angepasst werden, wie in
Zusätzlich muss in der Konfiguration \texttt{faces-config.xml} noch angepasst werden, wie in
\ref{lst:logger_factory_activate}, um die Factory durch das System aufrufen zu lassen.
\includecode[java]{chapters/thesis/appendix06_Logger.java}{lst:logger}{Vdi Logger}

View file

@ -3,10 +3,12 @@
\chapter{Einleitung}
\label{ch:intro}
\mytodos{Verena: Performance"=Verbesserung oder Perfromanceverbesserung?}
Die Akzeptanz und damit die Verwendung einer Software hängt von verschiedenen Kriterien
ab. Hierbei ist neben der Stabilität und der Fehlerfreiheit die Performance beziehungsweise
die Reaktionszeit der Software ein sehr wichtiges Kriterium. Hierfür muss sichergestellt
werden, dass die Anwendung immer in kurzer Zeit reagiert oder entsprechende Anzeigen dargestellt wird
werden, dass die Anwendung immer in kurzer Zeit reagiert oder entsprechende Anzeigen dargestellt werden
um eine längere Bearbeitung anzuzeigen.
%\section{Motivation}
@ -48,7 +50,7 @@ der Hochschule Darmstadt und der Fernuni Hagen umgesetzt und durch die Deutsche
Das entstandene Pilotprojekt ist eine webbasiert Anwendung, die aktuell unter \url{http://briefedition.wedekind.h-da.de}
eingesehen werden kann. Hierbei wurden sämtliche bislang bekannte Korrespondenzen in dem System digitalisiert. Die
Briefe selbst werden im etablierten TEI-Format gespeichert und über einen WYSIWYG-Editor von den Editoren und
Briefe selbst werden im etablierten TEI"=Format gespeichert und über einen WYSIWYG"=Editor von den Editoren und
Editorinnen eingegeben.
Das Projekt wurde anhand von bekannten und etablierten Entwurfsmustern umgesetzt um eine modulare und unabhängige
@ -61,10 +63,8 @@ Die aktuelle Umsetzung beinhaltet die bisher definierten Anforderungen vollstän
Recherchemöglichkeiten, sowie auch die Eingabe und die Verarbeitung der Briefe. Ein größeres Problem hierbei ist die
Performance der Oberfläche. Auf Grund der langen Abfragedauer des Datenbestandes leidet die Akzeptanz der Anwendung.
Das Ziel der Arbeit ist es, die Abfragedauer zu verringern, wodurch die Performance der Oberfläche signifikant
verbessert wird.
Hierbei ist auch ein Vergleich mit anderen Techniken angedacht.
Das Ziel dieser Arbeit ist es, die Abfragedauer zu verringern, wodurch die Performance der Oberfläche signifikant
verbessert wird. \mytodos{verena}
\section{Gliederung}
\label{sec:intro:structure}
@ -79,21 +79,22 @@ Hierzu gehören zum einen die Einstellungen der verwendeten Software, und zum an
Techniken in der Anwendung. Diese Techniken werden im weiteren Verlauf nochmal überprüft, ob eine alternative Lösung
einen performantere Umsetzung bringen kann.
Bei den Performance"=Untersuchung in \autoref{ch:performance-checking} werden nun die Konzepte angewandt, um
Bei den Performance"=Untersuchungen im \autoref{ch:performance-checking} werden nun die Konzepte angewandt, um
die Umgebung selbst zu untersuchen und die dort bekannten Probleme zu ermitteln. Diese werden direkt bewertet, unter den
Gesichtspunkten, ob eine Optimierung an dieser Stelle sinnvoll ist, oder ob der Arbeitsaufwand dafür zu aufwendig ist.
Gesichtspunkten, ob eine Optimierung an dieser Stelle sinnvoll ist oder ob der Arbeitsaufwand dafür zu aufwendig ist.
Zusätzlich werden noch die Vorbereitungen und die angepassten Konfigurationen für die nachfolgenden
Performance"=Untersuchung der Anwendung aufzeigt.
Performance"=Untersuchungen der Anwendung aufzeigt.
Zuerst wird im \autoref{ch:performance-investigation-application} die Ausgangsmessung durchgeführt, hierbei werden
alle bekannten Caches deaktiviert und eine Messung durchgeführt.
Dann werden Schicht für Schicht die Optimierungsmöglichkeiten aufgezeigt, umgesetzt und erneut gemessen. Diese Messung
wird dann in Abhängigkeit zur Ausgangsmessung die Optimierung bewertet.
wird mit der Ausgangsmessung verglichen und bewertet.
Nach der Optimierung kommt nun die Evaluierung im \autoref{ch:evaluation}. Hier werden die verschiedenen Optimierungen
begutachtet, in welchem Anwendungsfall die gewünschte Verbesserung in der Performance umgesetzt werden kann und welche
für den vorliegenden Fall in der Praxis umsetzbar ist.
Zum Abschluss im Kapitel \ref{ch:summary_and_outlook} wird explizit die Anpassungen dargestellt, die zu einer
merklichen Verbesserung geführt haben und wie diese entsprechend umgesetzt werden müssen. Zusätzliche wird
\todo{verena}
Zum Abschluss im \autoref{ch:summary_and_outlook} \texthl{werden alle Optimierungen zusammengefasst und eine Abwägung}
getroffen, unter welchen Gesichtspunkten die Anpassungen eine sinnvolle Optimierung darstellen. Zusätzlich wird
beschrieben wie ein weiteres Vorgehen durchgeführt werden kann.

View file

@ -6,16 +6,17 @@
Da die Anwendung als Webseite umgesetzt ist, ist der zugehörige Client für den Benutzer ein Webbrowser. Dies bedeutet,
dass jeder Wechsel einer Seite oder eine Suchanfrage als Web"=Request an den Server geschickt wird. Solch ein Web"=Request
geht durch mehrere Schichten des Server"=System bis die Antwort an den Client zurückgesendet wird, wie in
\ref{fig:webrequest} dargestellt.
\autoref{fig:webrequest} dargestellt.
Es wird ab hier immer von einem \textit{Glassfish}"=Server geredet. In der Praxis wird ein \textit{Payara}"=Server
\todo{die rede sein????}
Es wird ab hier immer von einem \textit{Glassfish}"=Server die rede sein. In der Praxis wird ein \textit{Payara}"=Server
verwendet. Der \textit{Glassfish}"=Server ist die Referenz"=Implementierung von Oracle, welche für Entwickler
bereitgestellt wird und die neuen Features unterstützt. Der \textit{Payara}"=Server ist aus dessen Quellcode entstanden
und ist für Produktivumgebungen gedacht, da diese mit regelmäßigen Aktualisierungen versorgt wird. In dem weiteren Text
wird weiterhin der Begriff \textit{Glassfish} verwendet.
und ist für Produktivumgebungen gedacht, da dieser mit regelmäßigen Aktualisierungen versorgt wird. In dem weiteren Text
wird der Begriff \textit{Glassfish} verwendet.
Angefangen bei der Anfrage die über den Webbrowser an den Server gestellt wird und vom \textit{Glassfish}"=Server
empfangen wird. In diesem wird anhand des definierten Routing entschieden, an welchen \textit{Controller} im \ac{JSF}
empfangen wird. In diesem wird anhand des definierten Routing entschieden, an welchen \textit{Controller} im \textit{\ac{JSF}}
die Anfrage weitergeleitet und verarbeitet wird. In diesem wird die Darstellung der Webseite geladen und die Anfragen
für den darzustellenden Datenbestand abgeschickt.
@ -25,7 +26,7 @@ an das unterlagerte Datenbankmanagementsystem \textit{PostgreSQL} weitergeleitet
DBMS werden bei Bedarf im \textit{OpenJPA Cache} aktualisiert.
Das \textit{PostgreSQL} besteht aus mehreren Teilen die ineinander greifen um die Anfragen zu bearbeiten. Dabei
sind die \textit{Memory Buffers} notwendig um den Zugriff auf die Festplatte zu reduzieren, um die Bearbeitungszeit
sind die \textit{Memory Buffers} notwendig um den Zugriff auf die Festplatte zu reduzieren um die Bearbeitungszeit
zu verringern. Um Anfragen die den Zugriff auf die Festplatte benötigen effizienter zu gestalten, bereiten die
\textit{Services} die Datenstrukturen auf.
@ -36,7 +37,7 @@ zu verringern. Um Anfragen die den Zugriff auf die Festplatte benötigen effizie
every fit/.style={inner sep=1em,draw}
]
%https://docs.oracle.com/javaee/6/tutorial/doc/bnacj.html
\node (browser) [block] {WebBrowser};
\node (browser) [block] {Webbrowser};
\node (fitClient) [fit=(browser)] {};
\node [left] at (fitClient.west) {Client};
@ -73,11 +74,11 @@ zu verringern. Um Anfragen die den Zugriff auf die Festplatte benötigen effizie
In den Java"=EE"=Anwendungen wird der \textit{Persistenzkontext} für die Anfragen vom \textit{Application"=Server}
bereitgestellt. Hierfür werden \textit{Application"=Server} wie \textit{GlassFish} genutzt, um die Verwendung eines Pools
von Datenbankverbindungen zu definieren \citep[68]{MüllerWehr2012}. Dadurch kann die Anzahl der Verbindung geringer
von Datenbankverbindungen zu definieren \citep[68]{MüllerWehr2012}. Dadurch kann die Anzahl der Verbindungen geringer
gehalten werden als die Anzahl der Benutzer die an der Anwendung arbeiten. Zusätzlich werden die Transaktionen über
\textit{\ac{SFSB}} gehandhabt, welche automatisch vor dem Aufruf erzeugt und danach wieder gelöscht
werden. Dies birgt allerdings den Nachteil, dass der \textit{Persistenzkontext} sehr groß werden kann, wenn viele
Entities in den \textit{Persistenzkontext} geladen werden. Da dies häufig zu Speicher"~ und damit Performanz"=Problemen
Entities in den \textit{Persistenzkontext} geladen werden. Da dies häufig zu Speicher"~ und damit Performance"=Problemen
\citep[79]{MüllerWehr2012} führen kann, muss hier darauf geachtet werden, nicht mehr benötigte Entities aus dem
\textit{Persistenzkontext} zu lösen.
@ -85,40 +86,41 @@ Entities in den \textit{Persistenzkontext} geladen werden. Da dies häufig zu Sp
\label{sec:basics:jpa}
Die \textit{\ac{JPA}} wird als First"=Level"=Cache in Java"=EE"=Anwendung verwendet, hier nehmen die
Objekte einen von vier Zuständen ein \citep[57]{MüllerWehr2012}. Im Zustand \textit{Transient} sind die Objekt erzeugt,
Objekte einen von vier Zuständen ein \citep[57]{MüllerWehr2012}. Im Zustand \texttt{Transient} sind die Objekte erzeugt,
aber noch nicht in den Cache überführt worden. Wenn diese in den Cache überführt worden sind, nehmen sie den Zustand
\textit{Verwaltet} ein. Ist das Objekt aus dem Cache und der Datenbank entfernt worden, nimmt es den Zustand
\textit{Gelöscht} an. \textit{Losgelöst} ist der letzte Zustand, bei dem das Objekt aus dem Cache entfernt worden ist,
\texttt{Verwaltet} ein. Ist das Objekt aus dem Cache und der Datenbank entfernt worden, nimmt es den Zustand
\texttt{Gelöscht} an. \texttt{Losgelöst} ist der letzte Zustand, bei dem das Objekt aus dem Cache entfernt worden ist,
aber nicht aus der Datenbank.
Eine Menge von Objekten wird als \textit{Persistenzkontext} bezeichnet. Solange die Objekte dem
\textit{Persistenzkontext} zugeordnet sind, also den Zustand \textit{Verwaltet} besitzen, werden diese auf Änderungen
überwacht, um sie am Abschluss mit der Datenbank zu synchronisieren. In der Literatur wird hierzu der Begriff
\textit{Persistenzkontext} zugeordnet sind, also den Zustand \texttt{Verwaltet} besitzen, werden diese auf Änderungen
überwacht, um diese am Abschluss mit der Datenbank zu synchronisieren. In der Literatur wird hierzu der Begriff
\textit{Automatic Dirty Checking} verwendet \citep[61]{MüllerWehr2012}.
\section{Glassfish - OpenJPA Cache}
\label{sec:basics:ojpac}
\todo{... die Verwendung spricht... - das spricht umformen}
Zusätzlich kann im \textit{JPA} ebenfalls noch der \textit{Second Level Cache} (L2-Cache) aktiviert werden. Dieser steht
jedem \textit{Persistenzkontext} zur Verfügung und kann dadurch die Anzahl der Datenbankzugriffe deutlich reduzieren,
was bei langsamen Datenbank"=Anbindungen zu hohen Performance"=Gewinnen führen kann \citep[171]{MüllerWehr2012}.
Gegen die Verwendung spricht, dass die Daten im \textit{Second Level Cache} explizit über Änderungen informiert werden
müssen, welche sonst beim nächsten Aufruf veraltete Werte liefern. Ebenfalls benötigt so ein Cache einen höheren Bedarf
\texthl{Zu Beachten ist, dass die Daten im \textit{Second Level Cache} explizit über die Änderungen informiert werden
müssen, weil sonst beim nächsten Aufruf veraltete Werte geliefert werden.} Ebenfalls benötigt ein Cache einen höheren Bedarf
an Arbeitsspeicher, in dem die Daten parallel zur Datenbank bereitgestellt werden, daher ist die Benutzung nur
problemlos bei Entities möglich, auf die meist lesend zugegriffen wird.
problemlos bei Entities möglich, auf welche meist lesend zugegriffen wird.
In der OpenJPA"=Erweiterung für den L2-Cache, wird in \textit{Objekt-Cache} (in OpenJPA als \textit{DataCache}
bezeichnet) und Query-Cache unterschieden. Über die Funktionen \texttt{find()} und \texttt{refresh()} oder einer Query
In der OpenJPA"=Erweiterung für den L2-Cache, wird in \textit{Objekt"=Cache} (in OpenJPA als \textit{DataCache}
bezeichnet) und \textit{Query"=Cache} unterschieden. Über die Funktionen \texttt{find()} und \texttt{refresh()} oder einer Query
werden die geladenen Entities in den Cache gebracht. Davon ausgenommen sind \textit{Large Result Sets} (Abfragen die
nicht alle Daten auf einmal laden), \texttt{Extent}"=Technologien und Queries, die einzelne Attribute von Entities
zurückliefern, aber nicht das Entity selbst. Hierbei kann genau gesteuert werden, welche Entity in den Cache abgelegt
wird und welche nicht. Ebenfalls kann auf Klassenbasis der zugehörige Cache definiert werden, um eine bessere
werden und welche nicht. Ebenfalls kann auf Klassenbasis der zugehörige Cache definiert werden, um eine bessere
Last-Verteilung beim Zugriff zu ermöglichen \citep[314]{MüllerWehr2012}.
Im \textit{Query-Cache} werden die Abfragen beziehungsweise die Eigenschaften einer Abfrage und die zurückgelieferten Ids der
Entities gespeichert. Bei einen erneuten Aufruf dieser Abfrage werden die referenzierten Objekte aus dem
\textit{Objekt-Cache} zurückgegeben. Bei veränderten referenzierten Entities wird der \textit{Query-Cache} nicht
genutzt und die betroffenen Abfragen werden unverzüglich aus dem \textit{Query-Cache} entfernt
Im \textit{Query"=Cache} werden die Abfragen beziehungsweise die Eigenschaften einer Abfrage und die zurückgelieferten Ids der
Entities gespeichert. Bei einem erneuten Aufruf dieser Abfrage werden die referenzierten Objekte aus dem
\textit{Objekt"=Cache} zurückgegeben. Bei veränderten referenzierten Entities wird der \textit{Query"=Cache} nicht
genutzt und die betroffenen Abfragen werden unverzüglich aus dem \textit{Query"=Cache} entfernt
\citep[316]{MüllerWehr2012}.
Um zu prüfen, ob die Einstellungen sinnvoll gesetzt sind, kann in OpenJPA eine Cache"=Statistik abgefragt werden. Mit
@ -129,20 +131,20 @@ die Einstellungen an den Entities angepasst werden \citep{IbmOpenJPACaching2023}
\label{sec:basics:memorybuffers}
Die Speicherverwaltung des PostgreSQL"=Servers muss für Produktivsysteme angepasst werden \citep[34-38]{Eisentraut2013}.
Hierunter fallen die \textit{shared\_buffers} die bei circa 10 bis 25 Prozent des verfügbaren Arbeitsspeichers liegen
Hierunter fallen die \texttt{shared\_buffers} die bei circa 10 bis 25 Prozent des verfügbaren Arbeitsspeichers liegen
sollten. Mit dieser Einstellung wird das häufige Schreiben des Buffers durch Änderungen von Daten und Indexen auf die
Festplatte reduziert.
Die Einstellung \textit{temp\_buffers} definiert wie groß der Speicher für temporäre Tabellen pro
Die Einstellung \texttt{temp\_buffers} definiert wie groß der Speicher für temporäre Tabellen pro
Verbindung maximal werden darf und sollte ebenfalls überprüft werden. Ein zu kleiner Wert bei großen temporären Tabellen
führt zu einem signifikanten Leistungseinbruch, wenn die Tabellen nicht im Hauptspeicher, sondern in einer Datei
ausgelagert werden.
Der \textit{work\_mem} definiert die Obergrenze des zur Verfügung gestellt Hauptspeichers pro Datenbankoperation wie
effizientes Sortieren, Verknüpfen oder Filtern. Ebenso wird im Falle eines zu klein gewählten Speichers auf temporäre
Dateien auf der Festplatte ausgewichen, was signifikanten Leistungseinbrüchen zur Folge haben kann.
Der \texttt{work\_mem} definiert die Obergrenze des zur Verfügung gestellt Hauptspeichers pro Datenbankoperation wie
effizientes sortieren, verknüpfen oder filtern. Ebenso wird im Falle eines zu klein gewählten Speichers auf temporäre
Dateien auf der Festplatte ausgewichen, was signifikante Leistungseinbrüche zur Folge haben kann.
Die \textit{maintenance\_work\_mem} wird bei Verwaltungsoperationen wie Änderungen und Erzeugungen von Datenbankobjekten
Die \texttt{maintenance\_work\_mem} wird bei Verwaltungsoperationen wie Änderungen und Erzeugungen von Datenbankobjekten
als Obergrenze definiert. Die Wartungsaufgabe \texttt{VACUUM}, welche die fragmentierten Tabellen aufräumt und
somit die Performance hebt, beachtet die Obergrenze ebenfalls.
@ -153,7 +155,7 @@ Die Wartung des Datenbanksystems ist eine der wichtigsten Aufgaben und sollte re
durchgeführt werden, damit die Performance des Systems durch die Änderungen des Datenbestands nicht einbricht
\citep[75]{Eisentraut2013}. Hierfür gibt es den \texttt{VACUUM}"=Befehl, welcher entweder per Hand oder automatisch durch
das Datenbanksystem ausgeführt werden soll. Für die automatische Ausführung kann der maximal verwendete Speicher über
die Einstellung \textit{autovacuum\_work\_mem} gesondert definiert werden \citep{PostgresPro:Chap20.4:2023}.
die Einstellung \texttt{autovacuum\_work\_mem} gesondert definiert werden \citep{PostgresPro:Chap20.4:2023}.
Neben dem Aufräumen durch \texttt{VACUUM}, sollten auch die Planerstatistiken mit \texttt{ANALYZE}
\citep[83]{Eisentraut2013} aktuell gehalten werden, damit die Anfragen durch den Planer richtig optimiert werden können.
Für beide Wartungsaufgaben gibt es den Autovacuum"=Dienst, dieser sollte aktiv und richtig konfiguriert sein.
@ -169,11 +171,11 @@ Für weitere Optimierungen werden anschließend die Anfragen einzeln überprüft
Ausführungspläne der Abfrage zu analysieren \citep[252]{Eisentraut2013}, die verschiedenen Plantypen und ihre Kosten zu
kennen, sowie die angegeben Werte für die Plankosten zu verstehen \citep[24-30]{Dombrovskaya2021}.
Besonderes Augenmerk gilt dem Vergleichen des tatsächlich ausgeführten mit dem ursprünglichen Plan
\citep[254]{Eisentraut2013}. Eine der wichtigsten Kennzeichen hierbei ist, ob die Zeilenschätzung akkurat war,
größere Abweichungen weißen häufig auf veraltete Statistiken hin.
\citep[254]{Eisentraut2013}. Eine der wichtigsten Kennzeichen hierbei ist, ob die Zeilenschätzung akkurat war.
Größere Abweichungen weißen häufig auf veraltete Statistiken hin.
Um die Abfragen selbst zu optimieren, gibt es ein Vorgehen über mehrere Schritte \citep[304-308]{Dombrovskaya2021}.
Zuerst wird Unterschieden, ob es sich um eine \textit{Kurze} oder eine \textit{Lange} Abfrage handelt. Im Falle einer
Zuerst wird unterschieden, ob es sich um eine \textit{Kurze} oder eine \textit{Lange} Abfrage handelt. Im Falle einer
\textit{Kurzen} Abfrage, werden zuerst die Abfragekriterien überprüft. Sollte dies zu keiner Verbesserung führen,
werden die Indexe geprüft. Ist dies ebenso erfolglos, wird die Abfrage nochmals genauer analysiert und so
umgestellt, dass die restriktivste Einschränkung zuerst zutrifft.

View file

@ -11,34 +11,34 @@ auf mögliche Optimierungen untersucht und bewertet.
\section{Allgemeine Betrachtung des Systems}
\label{sec:concept:viewsystem}
Für die Untersuchung des Systems wird der direkte Zugang zum Server benötigt. Hierbei werden zuerst die im Kapitel
\ref{sec:basics:services} beschriebenen Einstellungen überprüft.
Für die Untersuchung des Systems wird der direkte Zugang zum Server benötigt. Hierbei werden zuerst die im
\autoref{sec:basics:services} beschriebenen Einstellungen überprüft.
Zuerst wird am PostgreSQL"=Server die Konfiguration der Speicher mit der Vorgabe für Produktivsysteme abgeglichen.
Hierunter fallen die Einstellungen für die \textit{shared\_buffers}, der bei einem Arbeitsspeicher von mehr als 1 GB
Hierunter fallen die Einstellungen für die \texttt{shared\_buffers}, der bei einem Arbeitsspeicher von mehr als 1 GB
circa 25\% des Arbeitsspeicher besitzen sollte \cite{PostgresC20.4:2024}.
Bei der Einstellung \textit{temp\_buffers} geht es um den Zwischenspeicher für jede Verbindung, die bei der Verwendung
von temporären Tabellen verwendet wird. Dieser Wert sollte auf dem Standardwert von 8 MB belassen werden. Und nur bei
Bei der Einstellung \texttt{temp\_buffers} geht es um den Zwischenspeicher für jede Verbindung, die bei der Verwendung
von temporären Tabellen verwendet wird. Dieser Wert sollte auf dem Standardwert von 8 MB belassen werden, lediglich bei
der Verwendung von großen temporären Tabellen verändert werden.
Der Speicher, der für eine Abfrage verwendet werden darf, wird über die Konfiguration \textit{work\_mem} gesteuert.
Der Speicher, der für eine Abfrage verwendet werden darf, wird über die Konfiguration \texttt{work\_mem} gesteuert.
Wenn der Speicher zu gering wird, werden die Zwischenergebnisse in temporäre Dateien ausgelagert. Der empfohlene Wert
berechnet sich aus \textit{shared\_buffers} dividiert durch \textit{max\_connections} \citep{ConfigTo12:online}.
Sollte der Berechnung außerhalb der Grenzwerte von 1 MB und 256 MB liegen, ist der jeweilige Grenzwert zu verwenden.
Um zu ermitteln, ob die Konfiguration richtig ist, muss im PostgreSQL die Einstellung \textit{log\_temp\_files} auf 0
berechnet sich aus \texttt{shared\_buffers} dividiert durch \texttt{max\_connections} \citep{ConfigTo12:online}.
Sollte die Berechnung außerhalb der Grenzwerte von 1 MB und 256 MB liegen, ist der jeweilige Grenzwert zu verwenden.
Um zu ermitteln, ob die Konfiguration richtig ist, muss im PostgreSQL die Einstellung \texttt{log\_temp\_files} auf 0
gesetzt werden. Mit dieser kann ermittelt, ob temporäre Dateien verwendet werden und deren Größe. Bei vielen kleineren
Dateien sollte der Grenzwert erhöht werden. Bei wenigen großen Dateien ist es ist sinnvoll den Wert so zu belassen.
Für die Wartungsaufgaben wie VACUUM oder dem erstellen von Indexen wird die Begrenzung über die Einstellung
\textit{maintenance\_work\_mem} gesetzt. Dieser Wert sollte 5\% des verfügbaren Arbeitsspeicher entsprechen und größer
als \textit{work\_mem} sein.
Für die Wartungsaufgaben wie VACUUM oder dem Erstellen von Indexen wird die Begrenzung über die Einstellung
\texttt{maintenance\_work\_mem} gesetzt. Dieser Wert sollte 5\% des verfügbaren Arbeitsspeicher entsprechen und größer
als \texttt{work\_mem} sein.
Dann wird mit dem Systemtools, wie den Konsolenanwendungen \textit{htop} und \textit{free}, die Auslastung des Servers
Nachfolgend wird mit dem Systemtools, wie den Konsolenanwendungen \textit{htop} und \textit{free}, die Auslastung des Servers
überprüft. Hierbei ist die CPU"=Leistung, der aktuell genutzte Arbeitsspeicher, sowie die Zugriffe auf die Festplatte
die wichtigen Faktoren zur Bewertung.
Die CPU"=Leistung sollte im Schnitt nicht die 70\% überschreiten, für kurze Spitzen wäre dies zulässig. Da sonst der
Die CPU"=Leistung sollte im Schnitt 70\% nicht überschreiten, für kurze Spitzen wäre dies zulässig. Da sonst der
Server an seiner Leistungsgrenze arbeitet und dadurch es nicht mehr schafft die gestellten Anfragen schnell genug
abzuarbeiten.
@ -48,14 +48,14 @@ wodurch der aktuell nicht verwendete in die Page"=Datei ausgelagert wird. Hierdu
diese Elemente drastisch.
Die Zugriffsgeschwindigkeit, die Zugriffszeit sowie die Warteschlange an der Festplatte zeigt deren Belastungsgrenze auf.
Hierbei kann es mehrere Faktoren geben. Zum einem führt das Paging des Arbeitsspeicher zu erhöhten Zugriffen. Ein zu
klein gewählter Cache oder gar zu wenig Arbeitsspeicher erhöhen die Zugriffe auf die Festplatte, da weniger
In diesem Fall kann es mehrere Faktoren geben. Zum einem führt das Paging des Arbeitsspeicher zu erhöhten Zugriffen.
Ein zu klein gewählter Cache oder gar zu wenig Arbeitsspeicher erhöhen die Zugriffe auf die Festplatte, da weniger
zwischengespeichert werden kann und daher diese Daten immer wieder direkt von der Festplatte geladen werden müssen.
\section{Untersuchung der Anwendung}
\label{sec:concept:softwarestructure}
Bei der Performance-Untersuchung der Anwendung, wird sich im ersten Schritt auf die Dokumentenliste beschränkt. Anhand
Bei der Performance"=Untersuchung der Anwendung, wird sich im ersten Schritt auf die Dokumentenliste beschränkt. Anhand
dieser können die Optimierungen getestet und überprüft werden. Im Nachgang können die daraus gewonnenen Kenntnisse auf
die anderen Abfragen übertragen werden.
@ -71,8 +71,8 @@ abgelaufen ist. Ebenso wird nach Standorten sortiert, um zu ermitteln welchen Pe
Ort aufgehalten haben.
Da die Daten in der 3. Normalform in der Datenbank gespeichert werden, sind einige Relationen für die Abfragen
notwendig. Dies wird durch die generische Abfrage in \ref{lst:documentlist} gezeigt. Zusätzlich wird für jedes
dargestellte Dokument eine zusätzliche Abfrage durchgeführt, die in \ref{lst:documentlist_sub} zeigt, dass auch hier
notwendig. Dies wird durch die generische Abfrage in \autoref{lst:documentlist} gezeigt. Zusätzlich wird für jedes
dargestellte Dokument eine zusätzliche Abfrage durchgeführt, die in \autoref{lst:documentlist_sub} zeigt, dass auch hier
weitere Relationen notwendig sind.
\includecode[SQL]{chapters/thesis/chapter03_documentlist.sql}{lst:documentlist}{Generische Abfrage der Dokumentenliste}
@ -82,7 +82,7 @@ Nach aktuellem Stand beinhaltet die Datenbank circa 5400 Briefe, für die jeweil
gespeichert werden. Diese Graphik-Dateien werden im TIFF-Format abgespeichert und benötigen zwischen 1 und 80 MB
Speicherplatz. Dadurch kommt die Datenbank aktuell auf circa 3,8 GB.
Wie im Kapitel \ref{ch:basics} dargestellt, besteht die eigentliche Anwendung aus mehreren Schichten. Die
Wie im \autoref{ch:basics} dargestellt, besteht die eigentliche Anwendung aus mehreren Schichten. Die
PostgreSQL"=Schicht wurde schon im vorherigen Kapitel betrachtet. Daher gehen wir nun weiter nach oben in den Schichten
vom Glassfish"=Server.
@ -92,7 +92,7 @@ bereitgestellt, um die Daten Live vom Server verfolgen zu können. Zusätzlich w
aufgerufen und die Aufrufzeiten sowie andere externe Statistiken darüber erstellt und gespeichert.
In der \ac{JPA} Schicht sind die Anzahl der Entitäten im Persistence Context zu beobachten. Die Anzahl der verschiedenen
Klassen soll ermittelt und die Statistik"=Webseite um diese Daten erweitern. Um die Daten zu ermitteln, kann der
Klassen soll ermittelt werden und die Statistik"=Webseite um diese Daten erweitern. Um die Daten zu ermitteln, kann der
Quellcode aus \ref{lst:persistence-context-statistics} verwendet werden.
\begin{lstlisting}[language=Java,caption={Persistence"=Kontext Statistik},label=lst:persistence-context-statistics]
@ -112,11 +112,10 @@ Die Schicht \ac{EJB} besitzt keine Möglichkeit um eine sinnvolle Messung durchz
direkte Messungen eingefügt. Hier werden nur die externen Statistiken durch das Skript verwendet, um zu prüfen in
welchen Umfang die Umstellungen eine Veränderung im Verhalten der Webseite bewirken.
Bei den \ac{JSF} wird eine Zeitmessung eingefügt. Hierfür wird eine \textit{Factory} eingebaut, die sich in die
Verarbeitung der Seiten einhängt, und damit die Zeiten für das Ermitteln der Daten, das Zusammensetzen und das
Render der Sicht aufgenommen werden können. Die Zeiten werden in die Log"=Datei des \textit{Glassfish}"=Servers
hinterlegt und durch das Skript ausgewertet. Somit ist es einfach aufzuzeigen, an welcher Stelle der größte Teil
der Verzögerung auftritt.
Bei den \ac{JSF} wird eine Zeitmessung eingefügt. Um sich in die Verarbeitung der Seiten einzuhängen, wird eine
\textit{Factory} benötigt. In dieser werden die Zeiten zum Ermitteln der Daten, das Zusammensetzen und das Rendern
der Sicht ermittelt. Die Zeiten werden in die Log"=Datei des \textit{Glassfish}"=Servers hinterlegt und durch das
Skript ausgewertet. Somit ist es relativ leicht aufzuzeigen, an welcher Stelle der größte Teil der Verzögerung auftritt.
Die Abfragen werden ebenfalls untersucht und mit verschiedenen Methoden optimiert. Hierfür werden zum einen auf native
SQL"=Anfragen umgestellt und die Ausführungszeiten überprüft. Ebenfalls werden die Abfragen durch Criteria API erzeugt
@ -124,16 +123,16 @@ und dessen Ausführungszeit ermittelt.
Zusätzlich werden im SQL-Server Optimierungen vorgenommen, darunter zählen die \textit{Materialized View}, welche eine
erweiterte Sicht ist. Neben der Abfrage der Daten beinhalteten diese auch noch vorberechneten Daten der Abfrage, womit
diese viel schneller abgefragt werden können. Zusätzlich werden die cached queries überprüft ob diese eine Verbesserung
diese viel schneller abgefragt werden können. Zusätzlich werden die cached queries überprüft, ob diese eine Verbesserung
der Performance und der Abfragedauern verkürzen können.
Damit die Messungen nachvollziehbar bleiben, werden die Testaufrufe durch ein Bash-Script automatisiert gerufen.
Wichtig hierbei ist, das die Webseite immer vollständig gerendert vom Server an den Client übertragen wird.
Wichtig hierbei ist, dass die Webseite immer vollständig gerendert vom Server an den Client übertragen wird.
Somit kann die clientseitige Performance ignoriert werden, da alles Daten direkt in dem einen Aufruf bereitgestellt
wird. In dem Skript werden zum einen die Laufzeiten der Webanfragen ermittelt und die kürzeste, die längste und die
durchschnittliche Laufzeit ermittelt. Aufgrund der Speicherprobleme, werden auch die Speicherbenutzung des
durchschnittliche Laufzeit ermittelt. Auf Grund der Speicherprobleme, werden auch die Speicherbenutzung des
\textit{Glassfish}"=Servers vor und nach den Aufrufen ermittelt. Zum Schluss werden noch die Log"=Dateien des
\textit{PostgreSQL}"=Servers über das Tool \textit{pgBadger} analysiert und als Bericht aufbereitet.
Um die Netzwerklatenz ignorieren zu können, wird das Skript auf dem gleichen Computer aufgerufen, auf dem der Webserver
gestartet wurde. Das zugehörige Script ist im Anhang \ref{ap:timing} zu finden.
gestartet wurde. Das zugehörige Script ist im \autoref{ap:timing} zu finden.

View file

@ -4,7 +4,7 @@
\label{ch:performance-checking}
Für die Untersuchung der Performance"=Probleme sollten einige Vorbereitungen getroffen werden. Dazu gehören die
Konfigurationen des Servers und in welcher Art und Umfang Anpassungen für die Performance"=Messung an der Software
Konfigurationen des Servers und in welcher Art und Umfang Anpassungen für die Performance"=Messungen an der Software
durchgeführt werden müssen. Hierbei ist zu beachten, dass die Anpassungen minimal sind, damit die Messung selbst nicht
das Ergebnis verfälscht. Zum Abschluss wird noch auf die Untersuchung der Abfragen eingegangen, wie diese im
PostgreSQL"=Server durchgeführt wird.
@ -34,22 +34,22 @@ Zusätzlich sollte noch die aktuelle Auslastung des Server überprüft werden.
Als Server wird hier ein Redhat"=Server mit der Standard"=Konfiguration des PostgreSQL"=Server 10 verwendet. Daher
wird von einer richtigen Konfiguration der Speicher ausgegangen. Ebenfalls wird davon ausgegangen, dass der
automatische Dienst für \textit{VACUUM} und \textit{ANALYZE} aktiv ist. Eine weitere Überprüfung des Servers ist nicht
automatische Dienst für \texttt{VACUUM} und \texttt{ANALYZE} aktiv ist. Eine weitere Überprüfung des Servers ist nicht
möglich, da kein Zugang zum aktuellen Produktionsservers möglich ist.
\section{Einbau und Aktivieren von Performance-Messung}
\label{sec:performance-checking:performance-measure}
Um eine Messung der Performance in der Webseite durchführen zu können, gibt es in \ac{JSF} die Möglichkeit, über eine
eigene Implementierung der Klasse \textbf{ViewDeclarationLanguageWrapper} sich in das generieren der Webseite
einzuhängen. Hierbei können die Funktionen für das Erstellen, des Bauen und das Rendern der Webseite überschrieben
eigene Implementierung der Klasse \texttt{ViewDeclarationLanguageWrapper} sich in das Generieren der Webseite
einzuhängen. Hierbei können die Funktionen für das Erstellen, das Bauen und das Rendern der Webseite überschrieben
werden. In den überschriebenen Funktionen werden nun Laufzeiten gemessen und die ermittelten Zeiten mit einer Kennung
in die Log"=Datei eingetragen. Durch die Kennung, können die Zeiten im Nachgang über ein Script ermittelt und
ausgewertet werden.
Zusätzlich wird noch eine Implementierung der zugehörigen Factory"=Klasse \textbf{ViewDeclarationLanguageFactory}
Zusätzlich wird noch eine Implementierung der zugehörigen Factory"=Klasse \texttt{ViewDeclarationLanguageFactory}
benötigt. Durch diese Factory"=Klasse wird der eigentlichen Wrapper mit der Performance-Messung in die Bearbeitungsschicht
eingehängt. Diese Implementierung wird dann noch in der \textbf{faces-config.xml} eingetragen, wie das in
eingehängt. Diese Implementierung wird dann noch in der \texttt{faces-config.xml} eingetragen, wie das in
\autoref{lst:activate-factory} gezeigt wird, damit die Factory durch das System aufgerufen wird.
\begin{lstlisting}[language=xml,caption={Einbindung Factory},label=lst:activate-factory]
@ -76,10 +76,10 @@ log_rotation_size = 100MB
\end{lstlisting}
Über die Konfiguration unter \autoref{lst:postgresql_logconf} wird definiert welche Werte protokolliert werden. Die
wichtigste Einstellung ist \textit{log\_min\_duration\_statement}, diese definiert ab welcher Laufzeit eine Abfrage
wichtigste Einstellung ist \texttt{log\_min\_duration\_statement}, diese definiert, ab welcher Laufzeit eine Abfrage
protokolliert werden soll. Mit dem Wert 0 werden alle Abfragen protokolliert. Alle weitere Einstellungen sind so
gesetzt, dass nicht unnötige Abfragen für die spätere Auswertung mit \textit{pgBadger} protokolliert werden.
Zusätzlich ist die Einstellung \textit{log\_temp\_files} auf 0 zu setzen. Dadurch werden alle erzeugten temporären
Zusätzlich ist die Einstellung \texttt{log\_temp\_files} auf 0 zu setzen, dadurch werden alle erzeugten temporären
Dateien und ihre Größe ebenfalls protokolliert. Diese Dateien entstehen, wenn der temporäre Puffer für die Abfrage
nicht ausreicht und die Zwischenergebnisse ausgelagert werden müssen.
@ -102,19 +102,19 @@ log_timezone = 'Europe/Berlin'
\section{Prüfung von Abfragen}
\label{sec:performance-checking:sql-query-checking}
Das untersuchen der protokollierten Abfragen auf Performance Optimierungen ist ein weiterer Bestandteil dieser Arbeit.
Das Schlüsselwort \textbf{EXPLAIN} ist im PostgreSQL vorhanden, um den Abfrageplan einer Abfrage zu ermitteln Und
darzustellen, um diesen zu untersuchen. Der Abfrageplan ist als Baum dargestellt, bei dem die Knoten die
unterschiedlichen Zugriffsarten darstellt. Die Verbindung der Knoten und der Aufbau zeigt die Operationen, wie
etwas Joins, Aggregierung und Sortierung, und die Reihenfolgen der Abarbeitung. Zusätzlich sind auch Zwischenschritte,
Das Untersuchen der protokollierten Abfragen auf Performance Optimierungen ist ein weiterer Bestandteil dieser Arbeit.
Das Schlüsselwort \texttt{EXPLAIN} ist im PostgreSQL vorhanden, um den Abfrageplan einer Abfrage zu ermitteln und
darzustellen, um diesen zu untersuchen. Der Abfrageplan ist als Baum dargestellt, bei welchem die Knoten die
unterschiedlichen Zugriffsarten darstellen. Die Verbindung der Knoten und der Aufbau zeigt die Operationen, wie
etwa Joins, Aggregierung und Sortierung, und die Reihenfolgen der Abarbeitung. Zusätzlich sind auch Zwischenschritte,
wie Zwischenspeicherungen ersichtlich. Zu jeder Operation gibt es neben dem Typ noch zusätzliche Informationen, wie
die geschätzten Anlauf"= und Gesamtkosten (\textit{costs}), die geschätzte Anzahl der Zeilen (\textit{rows}) und die
geschätzte Breite jeder Zeile (\textit{width}). Der Wert von \textit{costs} wird bei übergeordneten Knoten summiert.
Bei der Option \textit{ANALYZE} wird die Abfrage ausgeführt und die echten Werte und Laufzeiten angezeigt. Ohne dieser,
wird nur der Plan erstellt und dargestellt. Durch \textit{VERBOSE} werden zusätzliche Informationen zum Abfrageplan
mit dargestellt und mit \textit{BUFFERS} werden die Informationen über die Nutzung der Caches mit dargestellt. Um an
Ende noch eine Zusammenfassung mit anzuhängen, gibt es die Option \textit{summary}. Eine vereinfachte Form des Aufrufs
Bei der Option \texttt{ANALYZE} wird die Abfrage ausgeführt und die echten Werte und Laufzeiten angezeigt. Ohne diese,
wird nur der Plan erstellt und dargestellt. Durch \texttt{VERBOSE} wird der Abfrageplan um zusätzliche Informationen
angereichert. Die Option \texttt{BUFFERS} erweitert die Informationen über die Nutzung der Caches. Für eine
Zusammenfassung am Ende des Abfrageplans, gibt es die Option \texttt{summary}. Eine vereinfachte Form des Aufrufs
ist in \autoref{lst:explain-easy} dargestellt.
\begin{lstlisting}[language=SQL,caption={Aufruf von EXPLAIN},label=lst:explain-easy]
@ -122,36 +122,38 @@ EXPLAIN (ANALYZE, VERBOSE, BUFFERS, SUMMARY)
select * from document;
\end{lstlisting}
Die zwei bekanntesten Knotentypen sind \textit{Seq Scan} und \textit{Index Scan}. Beim \textit{Seq Scan} wird die
Tabelle Zeile für Zeile gelesen und wenn vorhanden nach den Bedingungen gefiltert, hierbei entsteht eine unsortierte
Liste, entsprechend sind die Startkosten niedrig. Die bessere Alternative ist der \textit{Index Scan}, bei dem der
Index nach den Kriterien durchsucht wird, was meist durch den Aufbau des Index als BTree (Multi"=Way Balanced Tree)
sehr schnell geht.
\todo{bei seq scan ... nochmal neu überlegen + Satz mit seigenden kosten fehlt}
Die zwei bekanntesten Knotentypen sind \texttt{Seq Scan} und \texttt{Index Scan}. \texthl{Wenn eine Tabelle Zeile für Zeile
gelesen wird,} zeigt der Abfrageplan einen \texttt{Seq Scan}"=Knoten an. Hierbei entsteht, unabhängig davon ob eine
Bedingung zum filtern vorhanden ist, eine unsortierte Liste dessen Startkosten entsprechend niedrig sind. Je weiter die
Liste durchlaufen wird, desto höher steigen die notwendigen Kosten. Die kostengünstigere Alternative ist der
\texttt{Index Scan}, bei dem der Index nach den Kriterien durchsucht wird, was meist durch den Aufbau des Index als
BTree (Multi"=Way Balanced Tree) rapide geht.
Eine weitere Optimierungsmöglichkeiten sind die Verwendung von Indexe. Diese sind aber mit bedacht zu wählen, da bei
Eine weitere Optimierungsmöglichkeit ist die Verwendung von Indexe. Diese sind aber mit Bedacht zu wählen, da bei
mehreren Indexen die sehr ähnlich sind, nicht immer der gewünschte Index bei der Abfrage verwendet wird. Auch bedeutet
ein Index bei jeder Änderung der Daten zusätzliche Arbeit, da dieser entsprechend mit gepflegt werden muss und auch
dessen Statistik muss regelmässig mit aktualisiert werden. Ebenfalls ist die Reihenfolge der Spalte in einem
dessen Statistik muss regelmässig aktualisiert werden. Ebenfalls ist die Reihenfolge der Spalte in einem
zusammengesetzten Index von Bedeutung. Als Grundlage sollte hier mit der Spalte gestartet werden, welche die größte
Einschränkung durchführt. Zusätzlich muss die Art des Index definiert werden, welche davon abhängig ist, mit welcher
Vergleichsoperation auf die Tabellenspalte zugegriffen wird.
Um größere und aufwendige Abfragen zu optimieren, bietet der PostgreSQL noch die Möglichkeiten von
\textit{Materialized View}. Diese sind sehr ähnlich zu Sichten, speichern zusätzlich die Ergebnisse in einer
tabellenähnlichen Form ab. Somit sind die Zugriff auf diese Daten häufig performanter als die eigentliche Abfrage.
Die Performance wird durch die zusätzliche Aktualisierung des Datenbestand erkauft und muss daher abgewägt werden,
was sinnvoller ist.
Um größere und aufwendigere Abfragen zu optimieren, bietet der PostgreSQL noch die Möglichkeit von
\textit{Materialized View}. Diese sind sehr ähnlich zu den Sichten, zusätzlich werden die Ergebnisse in einer
tabellenähnlichen Form abgespeichert. Somit sind die Zugriff auf diese Daten häufig performanter als die eigentliche Abfrage.
Daher muss abgewägt werden, ob die Performance-Verbesserung trotz der zusätzliche Aktualisierung des Datenbestandes
als sinnvoll erachtet werden kann.
\mytodos{das doch wieder raus? oder nur das mit create statistics drin lassen}
Zusätzlich kann über die Systemtabelle \textit{pg\_statistic} oder die lesbarere Systemsicht \textit{pg\_stats} die
Zusätzlich kann über die Systemtabelle \texttt{pg\_statistic} oder die lesbarere Systemsicht \texttt{pg\_stats} die
aktuelle statistischen Informationen über eine Tabelle und deren Spalten ermittelt werden. In dieser Tabelle werden
durch das \textit{ANALYZE} beziehungsweise \textit{VACUUM ANALYZE} Kommando die Informationen zum Anteil der
\textit{NULL}"=Werte (null\_frac), Durchschnittlichen Größe (avg\_width), unterschiedlicher Werte (n\_distinct) und
durch das \texttt{ANALYZE} beziehungsweise \texttt{VACUUM ANALYZE} Kommando die Informationen zum Anteil der
\texttt{NULL}"=Werte (null\_frac), Durchschnittlichen Größe (avg\_width), unterschiedlicher Werte (n\_distinct) und
weitere gesammelt und für die Erstellung der Abfragepläne verwendet \citep{PostgreS39:online}. Diese Information
sollte vor dem erstellen eines Index betrachtet werden.
Diese Informationen können noch durch das Kommando \textit{CREATE STATISTICS} erweitert werden, für einen besseren
Abfrageplan. Das aktivieren der zusätzlichen Statistiken sollten immer in Verbindung mit dem überprüfung des
Abfrageplans durchgeführt werden, um zu ermitteln ob die Anpassung zu einer Optimierung und keiner Verschlechterung
führt.
Diese Informationen können noch durch das Kommando \texttt{CREATE STATISTICS} erweitert werden, für einen besseren
Abfrageplan. Das Aktivieren der zusätzlichen Statistiken sollte immer in Verbindung mit der Überprüfung des
Abfrageplans durchgeführt werden, um zu ermitteln inwieweit die Anpassung zu einer Optimierung und keiner
Verschlechterung führt.

View file

@ -3,45 +3,45 @@
\chapter{Performance-Untersuchung der Anwendung}
\label{ch:performance-investigation-application}
Nun werden die unterschiedlichen Schichten betrachtet und möglichen Performance"=Verbesserungen untersucht und deren
Nun werden die unterschiedlichen Schichten betrachtet und mögliche Performance"=Verbesserungen untersucht und deren
Vor"= und Nachteile herausgearbeitet.
Für die Tests wird ein aktuelles Manjaro-System mit frisch installierten Payara als Serverhost und der IntelliJ IDEA
Für die Tests wird ein aktuelles Manjaro"=System mit frisch installierten Payara als Serverhost und der IntelliJ IDEA
als Entwicklungsumgebung verwendet. Der Computer ist mit einer Intel CPU i7-12700K, 32 GB Arbeitsspeicher und einer SSD
als Systemfestplatte ausgestattet.
Zur ersten Untersuchung und der Bestimmung der Basis-Linie, wurde das Script ohne eine Änderung an dem Code und der
Zur ersten Untersuchung und der Bestimmung der Basis-Linie, wurde das Script ohne eine Änderung am Code und der
Konfiguration mehrfach aufgerufen. Hierbei hat sich gezeigt, dass der erste Aufruf nach dem Deployment circa 1500 ms
gedauert hat. Die weiteren Aufrufe benötigen im Durchschnitt noch 600 ms. Beim achten Aufruf des Scripts hat der
Server nicht mehr reagiert und im Log ist ein OutOfMemoryError protokolliert worden.
Server nicht mehr reagiert und im Log ist ein \textit{OutOfMemoryError} protokolliert worden.
Nach einem Neustart des Servers, konnte das gleiche Verhalten wieder reproduziert werden. Daraufhin wurde das Test-Script
um die Anzeige der aktuellen Speicherverwendung des Payara-Servers erweitert und diese zeitgleich zu beobachten. Diese
Nach einem Neustart des Servers, konnte das gleiche Verhalten wieder reproduziert werden. Daraufhin wurde das Test"=Script
um die Anzeige der aktuellen Speicherverwendung des Payara"=Servers erweitert um diese zeitgleich zu beobachten. Diese
Auswertung zeigte, dass der Server mit circa 1500 MB RSS Nutzung an seine Grenzen stößt. Diese Grenzen wurde durch die
Konfigurationsänderung im Payara-Server von \texttt{-Xmx512m} auf \texttt{-Xmx4096m} nach oben verschoben. Nun werden
circa 60 Aufrufe des Scripts benötigt, damit der Server nicht mehr reagiert. Hierbei wird aber kein OutOfMemoryError
circa 60 Aufrufe des Scripts benötigt, damit der Server nicht mehr reagiert. Hierbei wird aber kein \textit{OutOfMemoryError}
in der Log-Datei protokolliert und der Server verwendet nun circa 4700 MB RSS. Bei allen Tests war noch mehr als die
Hälfte des verfügbaren Arbeitsspeichers des Computers ungenutzt.
Mit der Konfiguration \texttt{-Xmx} wird der maximal verwendbare Heap"=Speicher in der \ac{JVM} definiert.
Dies zeigt direkt, dass es ein Problem in der Freigabe der Objekte gibt, da dass erhöhen des verwendbaren
Dies zeigt direkt, dass es ein Problem in der Freigabe der Objekte gibt, da dass Erhöhen des verwendbaren
Arbeitsspeicher das Problem nicht löst, sondern nur verschiebt.
Für alle nachfolgenden Messungen wird das Skript im \autoref{ap:calling_script} verwendet, welches die einzelnen
Aufrufe steuert. Die Ergebnisse werden in eine Tabelle überführt, wie in der \autoref{tbl:measure-without-cache}.
Aufrufe steuert. Die Ergebnisse werden in eine Tabelle überführt, wie in \autoref{tbl:measure-without-cache}.
Hierbei werden die Aufrufzeiten der Webseite aus dem Skript für die Zeitmessung mit Mindest"~, Durchschnitt"~ und
Maximalzeit aufgenommen, hierbei ist eine kürzere Zeit besser. Zusätzlich wird die Anzahl der aufgerufenen SQL Abfragen
ermitteln, auch hier gilt, dass weniger Aufrufe besser sind. Als letztes wird noch der verwendete Arbeitsspeicher
ermittelt, auch hier gilt, dass weniger Aufrufe besser sind. Als letztes wird noch der verwendete Arbeitsspeicher
vom \textit{Glassfish}"=Server vor und nach dem Aufruf ermittelt und die Differenz gebildet, hierbei sollte im besten
Fall die Differenz bei 0 liegen. Dieser Aufbau gilt für alle weiteren Messungen. Zusätzlich werden noch die Laufzeiten
der \ac{JSF} ermittelt und die durchschnittlichen Zeiten mit in der Tabelle dargestellt, und auch hier ist es besser,
wenn die Zeiten kürzer sind.
Als Grundlage für die Vergleiche wurden eine Messung durchgeführt, bei der alle Caches deaktiviert wurden und keine
Als Grundlage für die Vergleiche wurde eine Messung durchgeführt, bei welcher alle Caches deaktiviert wurden und keine
Änderung am Code vorgenommen wurde. Das Ergebnis dieser Messung ist in \autoref{tbl:measure-without-cache} zu finden.
Diese zeigen auch direkt ein erwartetes Ergebnis, dass der erste Aufruf bedeutend länger dauert als die Nachfolgenden.
Diese zeigen auch direkt ein zu erwartendes Ergebnis, dass der erste Aufruf bedeutend länger dauert als die Nachfolgenden.
Ebenfalls sieht man eindeutig, dass die Anzahl der Anfragen nach dem ersten Aufruf immer die gleiche Anzahl besitzen.
Der Speicherbedarf steigt auch relative gleichmässig, was nicht recht ins Bild passt, da hier keine Objekte im Cache
Der Speicherbedarf steigt relative gleichmässig, was nicht recht ins Bild passt, da hier keine Objekte im Cache
gehalten werden sollten.
\begin{table}[!h]
@ -62,7 +62,7 @@ gehalten werden sollten.
\label{tbl:measure-without-cache}
\end{table}
Vor jedem weiteren Test-Lauf wurde die Domain beendet und komplett neugestartet, um mit einer frischen Instanz zu
Vor jedem weiteren Test"=Lauf wird die Domain beendet und komplett neugestartet, um mit einer frischen Instanz zu
beginnen. Hierbei ist aufgefallen, dass fast immer 62 Abfragen zur Startup-Phase dazugehört haben, unabhängig von den
konfigurierten Cache Einstellungen. Einige dieser Abfragen sind durch das Erstellen der \textit{Materialized View}
\textit{searchreference} und \textit{searchfulltext} erklärbar. Zusätzlich ist noch ein zyklischer Dienst
@ -71,22 +71,22 @@ und entsprechend einige Abfragen an die Datenbank absetzt. Da weder die Sichten
benötigt werden, wurde der Dienst und das Erstellen im Code für die weiteren Tests deaktiviert.
Da die Abfragezeiten auf der Datenbank zu gering waren, um eine Verbesserung feststellen zu können, wurde für den
PostgreSQL und den Payara-Server ein Docker-Container erzeugt und diese limitiert. Die Konfiguration ist im
PostgreSQL und den Payara"=Server ein Docker"=Container erzeugt und diese limitiert. Die Konfiguration ist in
\autoref{ap:docker_config} beschrieben.
Mit dem neuen Aufbau ergeben sich nun neue Messungen. Für den Speicherbedarf wird nun nicht mehr der benutzte
Mit dem neuen Aufbau ergeben sich nun eine neue Messung. Für den Speicherbedarf wird nun nicht mehr der benutzte
Speicher der Anwendung beobachtet, sondern die Speichernutzung des Docker-Containers für den Payara-Server. Auch hier
ist es besser, wenn es keine oder nur geringe Änderungen vor und nach dem Aufruf der Webseite gibt, ein steigender Wert
zeigt an, dass der verwendete Speicher nicht sauber freigegeben werden kann.
Für die Ausführungszeiten der SQL-Abfragen wurden nur die sechs Abfragen für die Darstellung der Tabelle beachtet.
Für die Ausführungszeiten der \ac{SQL}"=Abfragen wurden nur die sechs Abfragen für die Darstellung der Tabelle beachtet.
Hierzu zählt die Hauptabfrage der Dokumenten"=-Tabelle, die Ermittlung des letzten und ersten Eintrags in der Tabelle,
die Ermittlung der Adressen des Autors, die Ermittlung der Koautoren, die Ermittlung der Faksimile, sowie die Ermittlung
der Anzahl aller vorhandenen Dokumente.
Zusätzlich wird die Zeit des Rendern der Sicht gemessen. Hierbei wird zum einen die komplette Zeit des Renderns
ermittelt. Innerhalb des Rendern wird dann noch die Zeit gemessen, wie lange es benötigt, die Daten aus der Datenbank
zu laden, und in die Java-Objekte umzuformen.
zu laden, und in die Java"=Objekte umzuformen.
\begin{table}[h]
\centering
@ -112,11 +112,11 @@ zu laden, und in die Java-Objekte umzuformen.
\section{Caching im OpenJPA}
\label{sec:performance-investigation-application:caching-openjpa}
Die Cache-Einstellung von OpenJPA werden über die zwei Einstellungen \texttt{openjpa.DataCache} und
Die Cache"=Einstellung von OpenJPA werden über die zwei Einstellungen \texttt{openjpa.DataCache} und
\texttt{openjpa.QueryCache} konfiguriert. Bei beiden Einstellungen kann zuerst einmal über ein einfaches Flag
\textit{true} und \textit{false} entschieden werden ob der Cache aktiv ist. Zusätzlich kann über das Schlüsselwort
\textit{CacheSize} die Anzahl der Elementen im Cache gesteuert werden. Wird diese Anzahl erreicht, dann werden zufällige
Objekte aus dem Cache entfernt und in eine SoftReferenceMap übertragen. Bei der Berechnung der Anzahl der Element werden
\textit{CacheSize} die Anzahl der Elemente im Cache gesteuert werden. Wird diese Anzahl erreicht, dann werden zufällige
Objekte aus dem Cache entfernt und in eine SoftReferenceMap übertragen. Bei der Berechnung der Anzahl der Elemente werden
angeheftete Objekte nicht beachtet.
Die Anzahl der Soft References kann ebenfalls über eine Einstellung gesteuert werden. Hierfür wird die Anzahl der
@ -153,7 +153,7 @@ wachsenden Speicherbedarfs nur nicht erklärt werden.
\label{tbl:measure-ojpa-active}
\end{table}
Bei einer erhöhten Cache-Größe, von 1000 auf 10000, zeigt sich auf den ersten Blick ein noch besseres Bild ab, wie in
Bei einer erhöhten Cache"=Größe, von 1000 auf 10000, zeigt sich auf den ersten Blick ein noch besseres Bild ab, wie in
\autoref{tbl:measure-ojpa-active-bigger} ersichtlich ist. Der erste Aufruf entspricht der Laufzeit mit geringerer
Cache"=Größe, aber schon die Anfragen an die Datenbank gehen drastisch zurück. Bei den weiteren Aufrufen werden im
Schnitt nun nur noch 6 Anfragen pro Seitenaufruf an die Datenbank gestellt, wodurch die Laufzeit im Schnitt nochmal
@ -213,11 +213,11 @@ der Objekte den Cache übersteigt, fällt die Verbesserung geringer aus.
\section{Cached Queries}
\label{sec:performance-investigation-application:cached-query}
Über die Einstellung \textit{openjpa.""jdbc.""QuerySQLCache} wird der Cache für abfragen aktiviert. Hierbei können
Über die Einstellung \textit{openjpa.""jdbc.""QuerySQLCache} wird der Cache für Abfragen aktiviert. Hierbei können
Abfragen angeben werden, die aus dem Cache ausgeschlossen werden. Der QueryCache wiederrum beachtet aber nur Abfragen
die keine Parameter verwenden. Das sieht man auch entsprechend der Auswertung der Aufrufe in der
\autoref{tbl:measure-cached-queries}, dass hier keine Veränderung der Aufrufzeiten stattgefunden hat. Gleich ob man
mit \ac{JPQL} oder mit der Criteria API abfragt.
die keine Parameter verwenden. Das sieht man auch entsprechend der Auswertung der Aufrufe in
\autoref{tbl:measure-cached-queries}, dass hier keine Veränderung der Aufrufzeiten stattgefunden hat, gleich ob
mit \ac{JPQL} oder Criteria API abfragt wird.
% document, documentaddresseeperson, first/last, documentcoauthorperson, count und documentfacsimile
\begin{table}[h!]
@ -247,7 +247,7 @@ mit \ac{JPQL} oder mit der Criteria API abfragt.
Der Ehcache ist ein L2"=Cache den man direkt in OpenJPA mit integrieren kann. Hierfür sind einige Punkte zu beachten.
Zum einen muss die Reference auf das \textit{ehcache} und das \textit{ehcache"=openjpa} Packet hinzugefügt werden.
Zusätzlich dazu sind die Konfiguration \textit{openjpa.""QueryCache}, \textit{openjpa.""DataCache} und
\textit{openjpa.""DataCacheManager} auf \textit{ehcache} anzupassen. Anhand der Annotation \textbf{@DataCache} kann
\textit{openjpa.""DataCacheManager} auf \textit{ehcache} anzupassen. Anhand der Annotation \texttt{@DataCache} kann
an jeder Klasse die Benennung des Caches sowie die Verwendung selbst gesteuert werden. Es wird für jede Klasse ein
eigener Cache angelegt und der Name auf den vollen Klassennamen gesetzt. Die Verwendung ist für alle Klassen
aktiviert und müssen explizit deaktiviert werden, wenn dies nicht gewünscht ist. Als letztes muss noch der
@ -255,10 +255,10 @@ Cache"=Manager aktiviert werden, dieser kann entweder durch Code programmiert we
in der \textit{ehcache.xml}.
Anhand der Auswertung von \ref{tbl:measure-ehcache-active} sieht man, dass der Ehcache einen starke Performance
Verbesserung aufbringt. Über die Performance"=Statistik"=Webseite kann beobachtet werden, dass bei gleichen Aufruf
Verbesserung aufbringt. Über die Performance"=Statistik"=Webseite kann beobachtet werden, dass bei gleichen Aufrufen
der Webseite nur die Treffer in Cache steigen, aber die Misses nicht. Ebenfalls erhöht sich die Anzahl der Objekte
im Cache nicht. Zusätzlich steigt in diesem Fall der Speicherverbrauch nur gering bis gar nicht. Zusätzlich zeigt sich,
das sich die Abfragezeiten in der Datenbank nur gering verkürzt wurden, aber die Laufzeit der Webseite sich starkt
im Cache nicht. Zusätzlich steigt in diesem Fall der Speicherverbrauch nur gering bis gar nicht. Des Weiteren zeigt sich,
dass sich die Abfragezeiten in der Datenbank nur gering verkürzt wurden, aber die Laufzeit der Webseite sich stark
verbessert hat. Dies lässt auch hier den Schluss zu, dass die Erstellung der Objekte im OpenJPA die meiste Zeit
benötigt.
@ -288,13 +288,13 @@ benötigt.
\section{Caching in EJB}
\label{sec:performance-investigation-application:caching-ejb}
Die Cache-Einstellungen des \ac{EJB} sind in der Admin-Oberfläche des Payara-Servers zu erreichen. Unter dem Punkt
Configurations $\Rightarrow$ server!=config $\Rightarrow$ EJB Container werden zum einem die minimalen und maximalen
Größen des Pools definiert werden. Ebenso wird an dieser Stelle die maximale Größe des Caches und die Größe der
Die Cache"=Einstellungen des \ac{EJB} sind in der Admin-Oberfläche des Payara-Servers zu erreichen. Unter dem Punkt
Configurations $\Rightarrow$ server"=config $\Rightarrow$ EJB Container werden zum einem die minimalen und maximalen
Größen des Pools definiert werden. Zum anderen wird an dieser Stelle die maximale Größe des Caches und die Größe der
Erweiterung definiert.
Anhand der Auswertung der \autoref{tbl:measure-ejb-cache-active} ist ersichtlich, dass der \ac{EJB}"=Cache keine
Auswirkung auf die Performance hat. Und es ist ersichtlich, dass die Anzahl der Datenbankabfragen nicht reduziert
Auswirkung auf die Performance hat. Ebenso ist es ersichtlich, dass die Anzahl der Datenbankabfragen nicht reduziert
wurden. Dies ist dadurch zu erklären, dass im \ac{EJB} die Provider gelagert werden, die über Dependency Injection
den Controller bereitgestellt werden. Die Objekt selbst werden nicht im \ac{EJB}"=Cache hinterlegt.
@ -316,7 +316,7 @@ den Controller bereitgestellt werden. Die Objekt selbst werden nicht im \ac{EJB}
\hline
\end{tabular}
}
\caption{Messung mit \ac{EJB}-Cache}
\caption{Messung mit EJB"=Cache}
\label{tbl:measure-ejb-cache-active}
\end{table}
@ -324,7 +324,7 @@ den Controller bereitgestellt werden. Die Objekt selbst werden nicht im \ac{EJB}
\label{sec:performance-investigation-application:query-jpql}
Für die \ac{JPQL} wird ein \ac{SQL} ähnlicher Syntax verwendet um die Abfragen an die Datenbank durchzuführen. Für die
Dokumentenliste wird der Code aus dem \autoref{lst:jpql-document-list-jpql} verwendet. Die Namen mit vorangestellten
Dokumentenliste wird der Code aus \autoref{lst:jpql-document-list-jpql} verwendet. Die Namen mit vorangestellten
Doppelpunkt sind Übergabevariablen.
\begin{lstlisting}[language=Java,caption={JPQL Dokumentenliste},label=lst:jpql-document-list-jpql]
@ -359,45 +359,46 @@ if(myResultList != null && !myResultList.isEmpty()) {
Da dieser Code direkt so aus dem Projekt kommt, wird hierfür keine gesonderte Zeitmessung durchgeführt, da diese der
Messung aus \autoref{tbl:measure-without-cache} entspricht.
Für die Optimierung wurden noch zusätzlich die Hints \textit{openjpa.""hint.""OptimizeResultCount},
\textit{javax.""persistence.""query.""fetchSize} und \textit{openjpa.""FetchPlan.""FetchBatchSize} gesetzt. Hierbei
Für die Optimierung wurden noch zusätzlich die Hints \texttt{openjpa.""hint.""OptimizeResultCount},
\texttt{javax.""persistence.""query.""fetchSize} und \texttt{openjpa.""FetchPlan.""FetchBatchSize} gesetzt. Hierbei
konnten je nach gesetzten Wert, keine relevanten Unterschiede festgestellt werden. Hierbei wurde der Wert auf zwei
gesetzt, welcher viel zu gering ist. Als weiterer Test wurde der Wert auf angefragte Größte gestellt und auf den
20"=fachen Wert der angefragten Größe.
Ebenso bringt der Hint \textit{openjpa.""FetchPlan.""ReadLockMode} auch keinen Unterschied bei der Geschwindigkeit.
Hierbei ist erklärbar, da im Standard bei einer reinen Selektion eine Lesesperre aktiv sein muss.
Bei \textit{openjpa.""FetchPlan.""Isolation} wird gesteuert, auf welche Sperren beim laden geachtet wird. Damit könnte
man zwar schreibsperren umgehen, und würde damit die Anfrage nicht mehr blockieren lassen, aber es führt unweigerlich
zu sogenannten \glqq Dirty"=Reads\grqq, wodurch die Ausgabe verfälscht werden könnte. Daher ist diese Einstellung sehr
Ebenso bringt der Hint \texttt{openjpa.""FetchPlan.""ReadLockMode} auch keinen Unterschied bei der Geschwindigkeit.
Hierbei ist erklärbar, das im Standard bei einer reinen Selektion eine Lesesperre aktiv sein muss.
Bei \texttt{openjpa.""FetchPlan.""Isolation} wird gesteuert, auf welche Sperren beim Laden geachtet wird. Damit könnte
man lediglich Schreibsperren umgehen, und würde damit die Anfrage nicht mehr blockieren lassen, jedoch führt es unweigerlich
zu sogenannten >>Dirty"=Reads<<, wodurch die Ausgabe verfälscht werden könnte. Daher ist diese Einstellung
mit Vorsicht zu verwenden.
Mit dem Hint \textit{openjpa.""FetchPlan.""EagerFetchMode} wird definiert, wie zusammengehörige Objekte abgefragt werden.
Mit dem Hint \texttt{openjpa.""FetchPlan.""EagerFetchMode} wird definiert, wie zusammengehörige Objekte abgefragt werden.
Bei dem Wert \textit{none} werden nur die Basis"=Daten abgefragt und jedes weitere Objekt wird in einem eigenen
Statement abgefragt. Mit \textit{join} wird definiert, dass abhängige Objekte die als \glqq to-one\grqq"=Relation
Statement abgefragt. Mit \textit{join} wird definiert, dass abhängige Objekte die als >>to-one<<"=Relation
definiert sind, in der Abfrage über einen Join verknüpft und damit direkt mitgeladen werden. Bei reinen
\glqq to-one\grqq"=Relation funktioniert das rekursive und spart sich damit einige einzelne Abfragen.
Bei der Einstellung \textit{parallel} wird für zwar für jedes abhängigen Objektdefinition eine Abfrage durchgeführt,
aber bei dieser wird der Einstieg über das Hauptobjekt durchgeführt. Somit muss in unserem Beispiel nicht für jedes
Dokument eine einzelne abfrage für die Koautoren durchgeführt werden, sondern es wird nur eine Abfrage abgesetzt für
alle Dokumente die ermittelt wurden. Technisch gesehen wird, die gleiche WHERE"=Abfrage nochmal durchgeführt und um
die JOINS ergänzt, um die Daten der Unterobjekte zu ermitteln.
Mit dem Hint \textit{openjpa.""FetchPlan.""SubclassFetchMode} ist die Konfiguration für Unterklassen definiert. Die
Möglichkeiten entsprechen der vom \textit{openjpa.""FetchPlan.""EagerFetchMode}.
>>to-one<<"=Relation funktioniert das Rekursiv und spart sich damit einige einzelne Abfragen.
Bei der Einstellung \textit{parallel} wird zwar für jede abhängige Objektdefinition eine Abfrage ausgeführt und
\texthl{diese werden direkt auf die Hauptobjekte gefiltert und die Verknüpfung im} OpenJPA"=Framework durchgeführt.
\todo{das wurde umgebaut}
Somit muss in unserem Beispiel nicht für jedes Dokument eine einzelne Abfrage für die Koautoren durchgeführt werden,
sondern es wird nur eine Abfrage abgesetzt für alle Dokumente die ermittelt wurden. Technisch gesehen wird die gleiche
WHERE"=Abfrage nochmal durchgeführt und um die JOINS ergänzt, um die Daten der Unterobjekte zu ermitteln.
Mit dem Hint \texttt{openjpa.""FetchPlan.""SubclassFetchMode} ist die Konfiguration für Unterklassen definiert. Die
Möglichkeiten entsprechen der vom \texttt{openjpa.""FetchPlan.""EagerFetchMode}.
Beim Umstellen der 2 Hints auf \textit{parallel} wird die Bearbeitungszeit fast halbiert und Anzahl der Datenbankaufrufe
wurde fast geviertelt. Dies zeigt, dass die einzelnen Aufrufe je Dokument aufwendiger sind, als eine komplette Abfrage
der abhängigen Daten und das zusammensetzen in der OpenJPA-Schicht.
Der letzte Hint \textit{openjpa.""FetchPlan.""MaxFetchDepth} schränkt die rekursive Tiefe ein, für die abhängige
Der letzte Hint \texttt{openjpa.""FetchPlan.""MaxFetchDepth} schränkt die rekursive Tiefe ein, für die abhängige
Objekte mitgeladen werden. Lediglich auf Grund fehlender Datenbestände wird die Abfrage beschleunigt.
\section{Abfragen Criteria API}
\label{sec:performance-investigation-application:query-criteria-api}
Für die Criteria API wird die Abfrage nicht in einem SQL-Dialekt beschreiben. Hierbei werden über Attribute die
Für die Criteria API wird die Abfrage nicht in einem SQL-Dialekt beschreiben, hierbei werden über Attribute die
Verlinkung zur Datenbank durchgeführt. An der Klasse selbst wird der Tabellenname definiert und an den Attributen die
Spaltennamen. Um die Anfrage durchführen muss nun nur noch Datenklasse angegeben werden und mit den Parametern
Spaltennamen. Um die Anfrage durchzuführen muss nun nur noch die Datenklasse angegeben und mit den Parametern
versorgt werden, wie es in \autoref{lst:criteria-api} gezeigt wird.
\begin{lstlisting}[language=Java,caption={Criteria API Dokumentenliste},label=lst:criteria-api]
@ -427,7 +428,7 @@ if (myResultList != null && !myResultList.isEmpty()) {
\end{lstlisting}
Wie in der Messung in \autoref{tbl:measure-criteria-api} zu sehen, unterscheiden sich die Abfragezeiten nur marginal
von denen mit \ac{JPQL}. Wenn man sich den Code im Debugger anschaut, sieht man auch, dass die zusammengesetzten
von denen mit \ac{JPQL}. Wenn man sich den Code im Debugger betrachtet, \texthl{ist zu erkennen,} dass die zusammengesetzten
Abfragen in den Java-Objekten fast identisch sind. Und in der Datenbank sind die Anfragen identisch zu denen über JPQL.
\begin{table}[h!]
@ -456,21 +457,21 @@ Implementierung ohne bedenken gegeneinander ausgetauscht werden, und die verwend
einfacher umzusetzen ist.
Bei den Hints ist es das gleiche wie bei \ac{JPQL}. Auch hier haben die meisten Hints keinen merkbaren Einfluss. Die
Einstellung \textit{openjpa.""FetchPlan.""EagerFetchMode} liefert auch hier Optimierungen, wenn der Wert auf
Einstellung \texttt{openjpa.""FetchPlan.""EagerFetchMode} liefert auch hier Optimierungen, wenn der Wert auf
\textit{parallel} gestellt wird. Hier wird ebenfalls die Anzahl der Anfragen reduziert und damit auch die
Geschwindigkeit optimiert.
\section{Materialized Views}
\label{sec:performance-investigation-application:materialized-views}
\textit{Materialized Views} sind Sichten in der Datenbank, die beim erstellen der Sicht den aktuellen Zustand ermitteln
und Zwischenspeichern. Somit wird beim Zugriff auf diese Sichten, nicht die hinterlegte Abfrage ausgeführt, sondern auf
\textit{Materialized Views} sind Sichten in der Datenbank, die beim Erstellen der Sicht den aktuellen Zustand ermitteln
und zwischenspeichern. Somit wird beim Zugriff auf diese Sichten, nicht die hinterlegte Abfrage ausgeführt, sondern auf
die gespeicherten Daten zugegriffen. Dies ist gerade bei vielen Joins von Vorteil. Zusätzlich können auf solchen
Sichten auch Indexe erstellt werden, um noch effektiver die Abfragen bearbeiten zu können.
Der größte Nachteil dieser Sichten ist, dass sie zyklisch oder bei Datenänderungen aktualisiert werden müssen, sonst
läuft der Datenbestand der Sicht und der zugrundeliegenden Abfrage auseinander. Da die Hauptarbeiten auf der Webseite
die Abfrage der Daten ist, und nicht das editieren, kann dieser Nachteil bei entsprechender Optimierung ignoriert werden.
die Abfrage der Daten ist, und nicht das Editieren, kann dieser Nachteil bei entsprechender Optimierung ignoriert werden.
In diesem Test wurde die aktuelle Implementierung aus dem Wedekind"=Projekt der \textit{Materialized View} inklusive
der Trigger und der \textit{SearchDocument}"=Klasse übernommen \citep{Dokument53:online}. Wie in
@ -586,7 +587,7 @@ CREATE INDEX idx_searchdocument_documentcategory
\end{lstlisting}
Für die Datenermittlung wurden die notwendigen Teile aus dem Wedekind"=Projekt kopiert. Bei der Darstellung wurden die
vorhanden Elemente die die Liste der Dokumente anzeigt kopiert und auf die \textit{SearchDocument}"=Klasse angepasst.
vorhanden Elemente, welche die Liste der Dokumente anzeigt, kopiert und auf die \texttt{SearchDocument}"=Klasse angepasst.
% document, first/last, documentaddresseeperson, documentcoauthorperson, documentfacsimile und count
% document, count, first/last
@ -614,13 +615,13 @@ vorhanden Elemente die die Liste der Dokumente anzeigt kopiert und auf die \text
Wie in \autoref{tbl:measure-materialized-view} zu sehen, bringt die Verwendung der \textit{Materialized View} eine
Verbesserung in verschiedenen Punkten. Zum einen ist eine Verbesserung der Aufrufzeiten zu erkennen, zusätzlich fällt
der Speicheranstieg weniger stark aus. Die Verbesserung der Aufrufzeiten lässt sich zusätzlich erklären, dass hier nun
nur noch vier statt der 6 Abfragen an die Datenbank gestellt werden, da die Einzelabfragen für die Adressen der
nur noch vier statt der sechs an die Datenbank gestellt werden, da die Einzelabfragen für die Adressen der
Personen und der Koautoren komplett entfallen.
Nach dem der Quellcode nochmal untersucht wurde, konnte man festellen, dass bei jeder Anfrage die gleiche Bedingung
benötigt wurde. Da die Sicht nun explizit für dies Anfrage geschaffen wurde, wurde die Bedingungen nun direkt in die
benötigt wurden. Da die Sicht nun explizit für dies Anfrage geschaffen wurde, wurde die Bedingungen nun direkt in die
Sicht mit integriert. Dies bedeutet eine Erweiterung der Sicht aus \autoref{lst:sql-materialized-view} um
\autoref{lst:sql-materialized-view-ext} und das entfernen der Parameter aus dem SQL-Anfragen im Java-Code.
\autoref{lst:sql-materialized-view-ext} und das Entfernen der Parameter aus dem SQL"=Anfragen im Java"=Code.
\begin{lstlisting}[language=SQL,caption={SQL Materialized View Erweiterung},label=lst:sql-materialized-view-ext]
WHERE d.validuntil > NOW()
@ -628,7 +629,7 @@ AND d.ispublishedindb = true;
\end{lstlisting}
Nach dem Anpassungen haben sich dann die Werte aus \autoref{tbl:measure-materialized-view-ext} ergeben. Diese Werte
zeigen nur minimale Unterschiede in den Zeiten, was auf Messtoleranzen zurückzuführen ist.
zeigen nur minimale Unterschiede in den Zeiten, diese sind auf Messtoleranzen zurückzuführen.
\begin{table}[h!]
\centering
@ -651,34 +652,35 @@ zeigen nur minimale Unterschiede in den Zeiten, was auf Messtoleranzen zurückzu
\label{tbl:measure-materialized-view-ext}
\end{table}
Da bei der \textit{Materialized View} das laden der Daten und das wandeln in die Java"=Objekte getrennt programmiert
Da bei der \textit{Materialized View} das Laden der Daten und das Wandeln in die Java"=Objekte getrennt programmiert
wurde, können hier eigene Zeitmessungen für die zwei Schritte eingebaut werden. Hierfür wird die Zeit vor dem
\textit{map}"=Aufruf und der \textit{map}"=Aufruf gemessen. Für den ersten Aufruf, wurde ein \textit{SearchDocument}
Objekt erzeugt und immer diese Objekt zurückgegeben. Damit wurde erst mal überprüft, wie lange das ermitteln der Daten
und das durcharbeiten der Ergebnisse bestimmt. Hierbei lagen die Zeiten bei circa 1 ms für das reine Datenladen und 3 ms
für den Aufruf der \textit{map}"=Funktion. Sowie innerhalb der \textit{map}"=Funktion pro Eintrag ein Objekt
erzeugt, noch ohne eine Konvertierung der ermittelten Daten in das Objekt, steigt die Laufzeit schon auf 54 ms.
Wenn man nun noch die Konvertierung der Daten wieder einbaut, steigt die Laufzeit nochmal auf nun 82 ms.
Dies zeigt, alleine das erzeugen der Objekt und der Json"=Parse Aufruf kostet die meiste Zeit.
\texttt{map}"=Aufruf und der \texttt{map}"=Aufruf gemessen. Für den erste Messung, wurde vor der Datenbankabfrage ein
\texttt{SearchDocument} Objekt erzeugt und dieses in jedem \texttt{map}"=Aufruf zurückgegeben. Mit dieser Aufbau wurde
\texthl{die Zeit ermittelt, um die Daten aus der Datenbank zu laden und dieses Ergebnis einmalig zu durchlaufen} ohne ein \todo{verena}
Objekt zu erstellen. Hierbei lagen die Zeiten bei circa 1 ms für das reine Laden der Daten und 3 ms
für den Aufruf der \texttt{map}"=Funktion. Sobald innerhalb der \texttt{map}"=Funktion pro Eintrag ein Objekt
erzeugt wird, ohne die Konvertierung der ermittelten Daten in das Objekt, steigt die Laufzeit schon auf 54 ms.
Wenn man nun noch die Konvertierung der Daten mit einbaut, erhöht sich die Laufzeit nochmals, auf nun 82 ms.
Alleine für das Erzeugen der Objekte und der Json"=Parse Aufrufe wird die meiste Zeit aufgewendet.
Bei der Verwendung des Hints \textit{openjpa.""FetchPlan.""FetchBatchSize} kann die Abfrage enorm verschlechtern. Wenn
Bei der Verwendung des Hints \texttt{openjpa.""FetchPlan.""FetchBatchSize} kann die Abfrage enorm verschlechtern. Wenn
dieser Wert zu klein oder groß definiert ist, wird die Laufzeit verschlechtert. Bei einem zu großen Wert wird die
Laufzeit der Datenbankanfrage auf circa 20 ms verlängert. Wenn der Wert zu gering gewählt ist, dann wird zwar die
Laufzeit der Datenbankanfrage minimal verkürzt, aber die \textit{map}"=Funktion wird dadurch verlängert.
Laufzeit der Datenbankanfrage minimal verkürzt, aber die \texttt{map}"=Funktion wird dadurch verlängert.
Das aktivieren der Cache"=Optionen wie in \autoref{sec:performance-investigation-application:caching-openjpa} oder in
Das Aktivieren der Cache"=Optionen wie in \autoref{sec:performance-investigation-application:caching-openjpa} oder in
\autoref{sec:performance-investigation-application:cached-query} dargestellt, haben keine Auswirkung auf die Performance.
Dies ist dadurch erklärbar, da keine Objekte durch das OpenJPA"=Framework erstellt werden, sondern erst in der
\textit{map}"=Funktion des eigenen Codes und daher wird der Cache nicht genutzt.
Dies ist dadurch erklärbar, das keine Objekte durch das OpenJPA"=Framework erstellt werden, sondern erst in der
\texttt{map}"=Funktion des eigenen Codes und daher wird der Cache nicht genutzt.
Wie schon ermittelt, benötigt das erstellen der Objekte den Großteil der Zeit für die Datenermittlung. Aufgrund dessen
wurde die übernommen \textit{SearchDocument}"=Klasse nochmal genauer betrachtet. Beim erstellen, werden die
Wie schon ermittelt, benötigt das Erstellen der Objekte den Großteil der Zeit für die Datenermittlung. Auf Grund dessen
wurde die übernommene \texttt{SearchDocument}"=Klasse abermals genauer betrachtet. Beim Erstellen werden die
\textit{Json}"=Daten direkt in Java"=Objekte gewandelt. Im ersten Schritt wird die Parse"=Funktion entfernt und die
Seite nochmals aufgerufen. Durch diese Umstellung fällt die Laufzeit der Datenermittlung auf circa 4 ms ab. Nun muss
noch geprüft werden, welche Zeit nun der Client zum parsen der \textit{Json}"=Daten benötigt. Hierfür werden die
Daten in einem versteckten \textbf{span}"=Element hinterlegt, wie es im \autoref{lst:jsf-datatable-json} zu sehen ist.
noch überprüft werden, welche Zeit nun der Client zum parsen der \textit{Json}"=Daten benötigt. Hierfür werden die
Daten in einem versteckten \texttt{span}"=Element hinterlegt, wie es im \autoref{lst:jsf-datatable-json} zu sehen ist.
Die hinterlegte \textit{CSS}"=Klasse ist zum auffinden der Elemente für den späteren Javascript. Das
\textbf{ajax}"=Element im Beispiel ist notwendig, damit bei einem Seitenwechsel die gleiche Interpreter"=Funktion für
\textit{ajax}"=Element im Beispiel ist notwendig, damit bei einem Seitenwechsel die gleiche Interpreter"=Funktion für
die \textit{Json}"=Daten aufgerufen wird, wie beim laden der Webseite.
\begin{lstlisting}[language=xml,caption={DataTable mit Json},label=lst:jsf-datatable-json]
@ -694,10 +696,10 @@ die \textit{Json}"=Daten aufgerufen wird, wie beim laden der Webseite.
</p:column>
\end{lstlisting}
Die Interpreter"=Funktion, welche in JavaScript geschrieben ist, wird benötigt um die ie übertragenen
Die Interpreter"=Funktion, welche in JavaScript geschrieben ist, wird benötigt um die übertragenen
\textit{Json}"=Daten in eine darstelle Form zu bringen. Die Funktion aus dem \autoref{lst:jsf-datatable-json-convert}
ermittelt erst alle versteckten Elemente, parsed den Inhalt und erstellt neue \textit{HTML}"=Elemente mit dem
darzustellenden Inhalt. Zusätzlich wird noch eine Zeitmessung mit eingebaut, um die Laufzeit am Client für das Rendern
ermittelt erst alle versteckten Elemente, deserialisiert den Inhalt und erstellt neue \textit{HTML}"=Elemente mit dem
darzustellenden Inhalt. Zusätzlich wird noch eine Zeitmessung eingebaut, um die Laufzeit am Client für das Rendern
in der Konsole anzuzeigen. Die Funktion wird nun direkt nach dem die Webseite fertig geladen wurde aufgerufen.
\begin{lstlisting}[language=javascript,caption={Wandeln von Json nach Html},label=lst:jsf-datatable-json-convert]
@ -741,16 +743,16 @@ $(document).ready(function() {
});
\end{lstlisting}
Da nun am Client Code ausgeführt wird, nachdem die Daten übertragen wurden, kann nicht mehr alles über das Script
Da nun am Client der Code ausgeführt wird, nachdem die Daten übertragen wurden, kann nicht mehr alles über das Script
durchgeführt werden. Daher werden nun die Laufzeiten am Server und am Client zusammenaddiert. Im Schnitt benötigt der
Aufruf auf der Serverseite nun 70 ms und am Client sind es circa 13 ms. Dies bedeutet addiert kommt man mit dieser
Lösung auf eine kürzere Laufzeit und weniger Last am Server.
Aufruf auf der Serverseite nun 70 ms und am Client sind es circa 13 ms. Die summierte Laufzeit von Client und Server
ist geringer als die reine Serverlösung und erzeugt gleichzeit weniger Last am Server. \mytodos{verena}
\section{Optimierung der Abfrage}
\label{sec:performance-investigation-application:optimizing-query}
Für die Optimierung der Abfrage werden diese zuerst mit \textit{explain}, wie in \autoref{lst:explain-diagnostic}
dargestellt, untersuchen. Für die einfachere Diagnose, wird der erstellte Plan mit Hilfe von
Für die Optimierung der Abfragen werden diese zuerst mit \texttt{EXPLAIN}, wie in \autoref{lst:explain-diagnostic}
dargestellt, untersucht. Für die einfachere Diagnose, wird der erstellte Plan mit Hilfe von
\textit{Postgres Explain Visualizer 2} (\url{https://github.com/dalibo/pev2}) visualisiert.
\begin{lstlisting}[language=SQL,caption={Explain für Diagnose},label=lst:explain-diagnostic]
@ -773,7 +775,7 @@ limit 400;
Die erstellte Visualisierung der Abfrage ist in \autoref{fig:explain-visualize} zu sehen. In der Visualisierung wurde
die Darstellung der Kosten gewählt, da ein Vergleich auf Basis der Zeit sehr schwierig ist und von äußeren Faktoren
abhängt, wie zum Beispiel dem Cache. Die Kosten sind stabiler und hängen in erster Linie vom Datenbestand ab.
abhängt, wie zum Beispiel dem Cache. Die Kosten sind stabiler und hängen in erster Linie von der Art des Datenbestandes ab.
\begin{figure}[h!]
\includegraphics[width=\linewidth]{gfx/chapter05_ExplainVisualize.png}
@ -782,19 +784,19 @@ abhängt, wie zum Beispiel dem Cache. Die Kosten sind stabiler und hängen in er
\end{figure}
In der Graphik ist zu sehen, dass zum einen die Hauptkosten im untersten Knoten \textit{Seq Scan} und einen der
obersten Knoten dem \textit{HashAggregate} liegen. Zusätzlich sieht man anhand der stärke von den Verbindungslinien der
Knoten, dass die Menge der Datensätze enorm hoch ist und dieser sich bis zum obersten Knoten durchzieht. Dies
obersten Knoten dem \textit{HashAggregate} liegen. Zusätzlich sieht man anhand der Stärke von den Verbindungslinien der
Knoten, dass die Menge der Datensätze enorm hoch ist und diese sich bis zum obersten Knoten durchziehen. Dies
bedeutet, dass die Einschränkung des Datenbestandes erst am Ende der Abfrage durchgeführt wird und diesbezüglich die
Dauer der Abfrage linear mit den Inhalt der \textit{document}"=Tabelle zusammenhängt. Des Weiteren wird für keine
Tabelle ein \textit{Index Scan} verwendet, sondern immer mit einem \textit{Seq Scan} gearbeitet, da durch das ermitteln
des kompletten Datenbestandes der Optimizer entscheidet, dass der komplette Scan der Tabelle kostengünstiger ist, als
Tabelle ein \textit{Index Scan} verwendet, sondern immer mit einem \textit{Seq Scan} gearbeitet, da durch das Ermitteln
des kompletten Datenbestandes der Optimizer entscheidet, ob der komplette Scan der Tabelle kostengünstiger ist, als
die Verwendung eines der vorhandenen Indexe. Dies kann durch den Befehl \lstinline[language=SQL]|SET enable_seqscan = off|
sehr einfach verifiziert werden. Damit wird die Verwendung von \textit{Seq Scan} deaktiviert und es wird dann ein
sehr einfach verifiziert werden. Damit wird die Verwendung von \textit{Seq Scan} deaktiviert und es wird anschließend ein
\textit{Index Scan} verwendet. Wenn man nun beide Pläne vergleicht sieht man die Erhöhung der Kosten bei der Verwendung
von \textit{Index Scan}.
Die beste Optimierung hierbei ist, die Menge der Datensätze so früh wie möglich einzuschränken. Da die Verwendung von
\textit{order by} innerhalb eines Sub"=Selects nicht erlaubt ist, verwenden wir hierfür eine \textit{Common Table
\texttt{order by} innerhalb eines Sub"=Selects nicht erlaubt ist, verwenden wir hierfür eine \textit{Common Table
Expression}, wie es in \autoref{lst:explain-optimize-cte} zu sehen ist. Zusätzlich wurde noch ein Index auf der
\textit{document}"=Tabelle für die Spalten der Bedingung und der Sortierung gesetzt, wie in
\autoref{lst:explain-optimize-cte-idx} zur sehen, damit in der \textit{Common Table Expression} nur der Index verwendet
@ -821,9 +823,9 @@ create index idx_document_with_stmt on document using btree
, startday desc, id );
\end{lstlisting}
Mit diesen Umstellungen erkennt man nun, dass die Kosten entsprechend gefallen sind. Ebenfalls konnten die Laufzeit um
Mit diesen Umstellungen erkennt man, dass die Kosten entsprechend gefallen sind. Ebenfalls konnten die Laufzeit um
mehr als den Faktor drei reduziert werden. Die Optimierung ist in \autoref{fig:explain-visualize-with} sehr deutlich
an dein dünneren Verbindungslinien zwischen den Knoten und der Umstellung von einigen \textit{Seq Scan} zu
an den dünneren Verbindungslinien zwischen den Knoten und der Umstellung von einigen \textit{Seq Scan} zu
\textit{Index Scan} ersichtlich. Zeitgleich ist auch der teure \textit{HashAggregate} nicht mehr im Abfrageplan
vorhanden.
@ -834,7 +836,7 @@ vorhanden.
\end{figure}
Bei der Untersuchung der Abfrage zur \textit{Materialized View} ist direkt herausgekommen, dass hier keine Optimierung
mehr möglich ist, da durch die definierten Index bei den aktuell möglichen Sortierkriterien direkt ein
mehr möglich ist, da durch die definierten Indexe bei den aktuell möglichen Sortierkriterien direkt ein
\textit{Index Scan} verwendet wird. Dies ist durch eine Überprüfung der Abfragepläne beweisbar, für diesen Fall wird
die Abfrage aus \autoref{lst:explain-search-document} verwendet.
@ -850,10 +852,10 @@ limit 400;
\end{lstlisting}
Der dazugehörige Abfrageplan ist in \autoref{lst:explain-search-document-output} zu sehen, hierbei ist die erste
Ausgabe mit dem erstellten Index und vor der zweiten Ausgabe wurde der Index deaktiviert. Anhand der Ausgabe ist zu
Ausgabe mit dem erstellten Index. Vor der zweiten Ausgabe wurde dieser Index deaktiviert. Anhand der Ausgabe ist zu
sehen, dass bei der Verwendung des Index weniger Operation notwendig sind und damit auch die teure Sortierung
eingespart werden konnte. Dies liegt daran, dass der Index entsprechend des Sortierkriterien definiert wurde Und
somit es möglich ist, direkt in den Index die Elemente in der richtigen Reihenfolge zu ermitteln.
eingespart werden konnte. Dies liegt daran, dass der Index entsprechend des Sortierkriterien definiert wurde und
somit ist es möglich, direkt in dem Index die Elemente in der richtigen Reihenfolge zu ermitteln.
Somit ist durch den Index der schnellstmögliche Zugriff gegeben.
\begin{lstlisting}[basicstyle=\scriptsize,caption={aa},label=lst:explain-search-document-output]

View file

@ -5,10 +5,10 @@
Nun werden die durchgeführten Anpassungen anhand ihre Effektivität betrachtet und unter welchen äußeren Einflüssen
diese eine Optimierung darstellen. Weiterhin werden die Nachteile der Anpassungen überprüft und und bei der Betrachtung
der Effektivität mit beachtet.
der Effektivität mit beachtet. \mytodos{verena}
Es wurden die Konfigurationen der Caches von OpenJPA, JPA und EJB aktiviert und deren Auswirkung betrachtet. Bei den
Caches, bei denen eine Größe angebbar ist, wurde zusätzlich mit der Anzahl variiert, um zu ermitteln in welchen Umfang
Caches, bei denen eine Größe angebbar ist, wurde zusätzlich mit der Anzahl variiert, um zu ermitteln in welchem Umfang
sich diese auswirkt. Des Weiteren wird die Art der Programmierung für die Abfragen betrachtet, ob signifikante
Unterschiede in der Performance und der Abarbeitung erkennbar sind. Als weiteren Punkt werden die Abfragen an die
Datenbank untersucht, um zu ermitteln ob diese durch Umstellung verbessert werden können. Abschließend werden die
@ -31,14 +31,14 @@ schon normalisiert, daher kann hier nichts weiter optimiert werden.
Eine weitere Optimierungsstrategie besteht in der Denormalisierung, um sich die Verknüpfungen der Tabellen zu sparen.
Dies ist in diesem Fall nicht anwendbar, da nicht nur 1:n Beziehungen vorhanden sind, sondern auch auch n:m Beziehungen.
Dadurch würden sich die Anzahl der Dokumentenliste erhöhen. Eine weitere Möglichkeit wäre es, die Duplikate auf der
Dadurch würde sich die Anzahl der Dokumentenliste erhöhen. Eine weitere Möglichkeit wäre es, die Duplikate auf der
Serverseite zusammenzuführen.
\section{Statische Webseiten}
\label{sec:evaluation:static-website}
Eine Umstellung der Dokumentenliste in statische Webseite, würde die Zugriffszeiten sehr verkürzen. Darüber hinaus
funktionieren in statischen Webseiten aber keine Suchen oder eine Sortierungen. Die Sortierung könnte durch das Erstellen
funktionieren in statischen Webseiten aber keine Suchen oder Sortierungen. Die Sortierung könnte durch das Erstellen
von statischen Seiten aller Möglichkeiten der Sortierung emuliert werden, diese würde den notwendigen Speicherbedarf der
Webseite vervielfachen. Für die Suchanfragen ist dies nicht mehr möglich, da nicht alle Suchanfragen vorher definiert
werden können.
@ -65,9 +65,9 @@ die notwendige Rechenleistung fehlt, um die Webseite in annehmbarer Zeit darzust
Die Aufteilung eines großen Datenbestandes in mehrere einzelne Seiten, ist eine der wenige Optimierungsmöglichkeiten in
der JSF"=Ebene. Dieser Einbau optimiert direkt an mehreren Stellen, dazu gehört die kleinere Datenmenge die vom
Datenbankserver geladen wird. Ebenso wird entsprechend weniger Zeit benötigt um die View zu erstellen und die zum
Client übertragene Datenmenge ist auch geringer. Dadurch benötigt die Seite auf dem Client auch weniger Zeit zum
rendern.
Datenbankserver geladen wird. Ebenso wird entsprechend weniger Zeit benötigt um die View zu erstellen,
gleichzeitig wir die übertragene Datenmenge an den Client reduziert. Dadurch benötigt die Seite auf dem Client weniger
Zeit zum rendern.
Da das Paging für den Fall der Dokumentenliste implementiert ist, gibt es hier keine weiteren offensichtliche
Optimierungsmöglichkeiten.
@ -85,7 +85,7 @@ Das Entfernen der Objekte kann zwar umgangen werden, indem die häufig abgefragt
den Speicherbedarf noch weiter erhöht, da diese Objekte nicht in die Zählung der Cache"=Objekte beachtet werden.
Was uns direkt zum größten Nachteil diese Caches kommen lässt, die notwendig Speichermenge die ständig zur Verfügung
gestellt werden muss. Damit ist immer ein gewisser Grundbedarf notwendig, da sich der Speicher bis zum eingestellten
Grenzwert aufbaut und dann nicht mehr entleeren wird. Gerade bei kleiner dimensionierten Servern stellt dies ein
Grenzwert aufbaut und dann nicht mehr entleert wird. Gerade bei kleiner dimensionierten Servern stellt dies ein
größeres Problem dar, da nun weniger Speicher für die anderen laufenden Programme, wie dem Datenbankmanagementsystem,
zur Verfügung steht.
@ -106,7 +106,7 @@ explizit im Cache aufgenommen und angepinnt werden.
Die Optimierung über die gespeicherten Anfragen brachte keine Verbesserung hervor. Dies ist dadurch erklärbar, dass
für die diese Art nur Anfragen verwendet werden, die keinerlei Bedingungen besitzen. In diesem Fall sind in der Tabelle
noch nicht freigegebene und ungültige Datensätze gespeichert, daher müssen diese vor dem Übertragen herausgefiltert
werden. Dies ist der Grund warum diese Anfragen in diesem Cache nicht gespeichert werden.
werden. Aus diesem Grund werden die Anfragen in diesem Cache nicht gespeichert werden.
Dadurch ist dieser Cache für eine Performance"=Verbesserung in dem Fall der Dokumentenliste nicht anwendbar.
@ -129,7 +129,7 @@ des OpenJPA Framework liegen muss.
Durch die effizienter Verwendung des Speichers, ist der Ehcache die bessere Alternative zum OpenJPA"=Cache. Dieser ist
auch schon für kleinere Serverkonfigurationen gut verwendbar. Hierbei ist nur abzuwägen, mit welcher Größe der Cache
bereitgestellt werden kann, was direkt am verfügbaren Arbeitsspeicher abhängt.
bereitgestellt werden kann, dies hängt direkt vom verfügbaren Arbeitsspeicher ab.
\section{Caching in EJB}
\label{sec:evaluation:ejb}
@ -154,7 +154,7 @@ von OptimizeResultCount, der FetchSize sowie der FetchBatchSize hilft dem Framew
effizient abzuarbeiten, konnte aber in den gemessenen Laufzeiten nicht verifiziert werden.
Anders verhält sich dies mit den Einstellungen für EagerFetchMode, welche definiert wie die Daten für abhängige Klasse
ermittelt werden. Bei Umstellung auf \textit{parallel} konnte für die Ermittlung der Dokumente einiges an Performance gewonnen
ermittelt werden. Bei der Umstellung auf \textit{parallel} konnte für die Ermittlung der Dokumente einiges an Performance gewonnen
werden. Das liegt daran, dass nun für die abhängigen Objekte, wie den Koautoren, nicht pro Dokument eine Anfrage an die
Datenbank gestellt wird, sondern es werden alle Koautoren für die ermittelten Dokumente auf einmal ermittelt. Die
Zuordnung der Koautoren zu dem Dokument wird dann nun im Framework und nicht mehr durch die Datenbank durchgeführt.
@ -164,7 +164,7 @@ Auf Grund dessen ist die Entscheidung der Technik für die Performance irrelevan
jeweiligen Einsatzzweck besser beziehungsweise einfacher zu programmieren ist. Das Setzen der richtigen Hints wiederrum
ist in beiden Fällen äußerst wichtig. Explizit bei der EagerFetchMode muss vorher darüber nachgedacht werden, wie viele
abhängige Objekttypen es zu dieser Klasse gibt, welche dazu geladen werden sollen und von welcher Anzahl an Objekte
ausgegangen werden kann. Gerade bei ein größeren Anzahl lohnt es sich den Hint auf \textit{parallel} zu setzen.
ausgegangen werden kann. Gerade bei einer größeren Anzahl lohnt es sich den Hint auf \textit{parallel} zu setzen.
Gleiches gilt dem Hint SubclassFetchMode, dieser steuert dimensionierte Abfragen im Falle von abgeleiteten Klassen.
\section{Materialized View}
@ -173,9 +173,9 @@ Gleiches gilt dem Hint SubclassFetchMode, dieser steuert dimensionierte Abfragen
Die Idee der \textit{Materialized View} ist simple aber sehr effizient, gerade für einen Datenbestand welcher häufig gelesen
und selten verändert wird. Hierbei werden komplexe Abfragen einmalig ausgeführt und das Ergebnis intern
zwischengespeichert. Für alle weiteren Aufrufe, werden die Daten nun aus der Zwischenspeicher gelesen und dem Aufrufer
zurückgegeben. Der größte Nachteil der \textit{Materialized View} ist, das bei einer Änderung an den Quelldaten die
zurückgegeben. Der größte Nachteil der \textit{Materialized View} ist, dass bei einer Änderung an den Quelldaten die
Sicht aktualisiert werden muss. Dieser Nachteil kommt in einer Briefedition nicht zum tragen, da in dieser nach dem die
Briefe einmalig eingepflegt wurden, noch selten Änderungen erfahren. Die Recherche über den Datenbestand die größte Zeit
Briefe einmalig eingepflegt wurden, nur noch selten Änderungen erfahren. Die Recherche über den Datenbestand die größte Zeit
gewidmet wird.
Ein weiterer Nachteil der \textit{Materialized View} ist die doppelte Speicherung der Daten, da die Daten für die Sicht
@ -185,12 +185,12 @@ die Faksimile, sind nicht in dieser Sicht enthalten und werden erst beim Anzeige
Zusätzlich ist zu beachten, dass bei der Verwendung eines Caches die Daten ebenfalls doppelt gehalten werden und in
den meisten Fällen im Arbeitsspeicher gehalten werden.
Eine weitere Optimierung, welche durch die \textit{Materialized View} entstanden ist, ist die direkte integration der
Eine weitere Optimierung, welche durch die \textit{Materialized View} entstanden ist, ist die direkte Integration der
Autoren, der Koautoren und der Adressen im \textit{Json}"=Format. Durch diese aus dem Wedekind-Projekt übernommene Idee
konnten schon viele zusätzliche Abfragen eingespart werden, da diese nicht mehr durch OpenJPA nach der Hauptabfragen
für jede Datenzeile einzeln durchgeführt wird.
Zusätzlich konnte dies nochmal beschleunigt werden, in dem das parsen der \textit{Json}"=Daten vom Server auf den Client
Zusätzlich konnte dies nochmal beschleunigt werden, in dem das Parsen der \textit{Json}"=Daten vom Server auf den Client
verlagert wurde. Hiermit konnte zum einen Last vom Server genommen werden und die gesamte Ausführungszeit nochmals
optimieren. Die Wandlung der Daten in \textit{HTML}"=Objekte ist eine Kernkompetenz von JavaScript und damit auch bei
schwächeren Clients in kurzer Zeit durchführbar.
@ -200,17 +200,17 @@ verwendet wird. Der größte Unterschied zur Standardimplementierung ist die Ver
zu erstellen und zu befüllen und es nicht durch das OpenJPA"=Framework durchführen zu lassen.
Dies legt den Schluss nahe, dass es Probleme in der Speicherverwaltung der Objekte im OpenJPA"=Framework existieren.
Zusammenfassend ist zu sagen, dass die \textit{Materialized View} eine gute Wahl sind, um die Listendarstellungen
Zusammenfassend ist zu sagen, dass die \textit{Materialized View} eine gute Wahl ist, um die Listendarstellungen
zu optimieren. Mit dieser Technik können zum einen die Abfragezeiten optimiert werden, wodurch gleichzeit die
Ressourcennutzung verringert wird. Zum anderen wird die Ressourcennutzung des Servers noch weiter reduziert, wenn die
\textit{Json}"=Verarbeitung an den Client ausgelagert wird.
Durch die doppelte Datenhalten muss bei jeder Abfrage geprüft werden, ob der Nutzung der \textit{Materialized View} sinnvoll
Durch die doppelte Datenhaltung muss bei jeder Abfrage geprüft werden, ob die Nutzung der \textit{Materialized View} sinnvoll
ist oder direkt auf denormalisierte Daten umgestellt werden sollte, weil der zusätzliche benötigte Speicher größer als
die Quelle ist.
Im Gegensatz zu einer reinen Cache"=Lösung die die gleiche Optimierung besitzt, ist diese vorzuziehen, da in den
meisten Fällen der Festplattenspeicher kostengünstiger als der Arbeitsspeicher ist. Zusätzlich ist der Cache begrenzt
und wirft alte Objekte aus dem Cache, wenn dieser voll ist und dadurch wird ein Zugriff auf so ein Objekt wieder
langsamer. Somit ist die Optimierung über die \textit{Materialized View} auf langezeit gesehen kostengünstiger und
und wirft alte Objekte aus dem Cache. Wenn dieser voll ist und wird ein Zugriff auf diese entfernten Objekte
langsamer. Somit ist die Optimierung über die \textit{Materialized View} auf lange Zeit gesehen kostengünstiger und
stabiler.
\section{Optimierung der Abfrage}
@ -219,7 +219,7 @@ stabiler.
Die Abfragen die durch die OpenJPA an die Datenbank abgesetzt werden, sind meist durch ihre Einfachheit gut optimiert.
Nur durch Sortierung oder Bedingungen können die Abfragen langsam werden. Diese können durch entsprechende Indexe
gelöst werden. Bei größeren Abfragen mit mehreren Joins kann durch geschicktes umstellen die Performance verbessert
werden. Die Hauptabfrage der Dokumentenliste ist eine mit mehreren Joins, und diese wurde explizit untersucht.
werden. Die Hauptabfrage der Dokumentenliste besteht aus mehreren Joins und diese wurde explizit untersucht.
Der Abfrageplan der Hauptabfrage wurde visuell untersucht und zeigt, dass das Hauptproblem die nicht eingeschränkte
Datenmenge der Haupttabelle \textit{document} ist. Dadurch werden zum einen die anderen Tabellen komplett dazu geladen
@ -233,9 +233,9 @@ Zeilenanzahl während der Verarbeitung enorm verringert werden, wodurch einige d
umgestellt wurden. Durch die Umstellung konnte die Abfragezeit um mehr als das dreifache reduziert wurde.
Mit dieser Art der Umstellung können Abfragen optimiert werden, die fürs Paging verwendet werden und die Abfrage aus
mehrere Tabellen besteht. Das wichtigste hierbei ist, dass die Bedingungen und die Sortierkriterien auf der
mehrere Tabellen besteht. Das Wichtigste hierbei ist, dass die Bedingungen und die Sortierkriterien auf der
Haupttabelle arbeiten. Wenn dem nicht so ist, müssen Joins in die \textit{Common Table Expression} mit aufgenommen
werden und damit funktioniert die Reduzierung der Datensätze nicht mehr. Bei der Selektion einer Tabelle hat diese Art
der Optimierung keine Auswirkung, hier hilft nur das geschickte setzen von Indexen auf die Tabelle, welche die
der Optimierung keine Auswirkung, hier hilft nur das geschickte Setzen von Indexen auf die Tabelle, welche die
Bedingungen und die Sortierkriterien beinhalten. Dies wurde mit der Untersuchung der Abfrage auf die
\textit{Materialized View} bestätigt.

View file

@ -10,18 +10,18 @@
% In der Gegenwart schreiben, außer bei verweisen auf die Arbeit, dann in der Vergangenheit!
Die Untersuchungen am Beispiel des Wedekind"=Projektes zeigen, dass mehrere Optimierungsmöglichkeiten in den
Briefedition existieren. Für die Untersuchung wurde sich auf die Dokumentenliste beschränkt und anhand dieser
Briefeditionen existieren. Für die Untersuchung wurde sich auf die Dokumentenliste beschränkt und anhand dieser
die Optimierung implementiert, untersucht und jeweils mit der Ausgangsmessung vergleichen. Für die Messung wurden
Skripte erstellt, die auf dem gleichen Computer wie der Webserver und der Datenbankserver laufen, damit diese auch
vergleichbar bleiben und externe Einflussfaktoren minimiert werden.
\mytodos{den Absatz hier drin lassen oder raus?}
%Messung zeigt wo die probleme liegen! hier noch ausformulieren
Durch die Ausgangsmessungen war erkennbar, dass der größte Teil der Verarbeitungszeit im bereitstellen der Entitäten
%Messung zeigt wo die Probleme liegen! hier noch ausformulieren
Durch die Ausgangsmessungen war erkennbar, dass der größte Teil der Verarbeitungszeit im Bereitstellen der Entitäten
benötigt wird. Die Messung der Abfragen auf der Datenbank wiederum konnte die hohe Verarbeitungszeit nicht bestätigen,
daher lag hier schon die Vermutung nahe, dass der Großteil der Zeit im ORM"=Mapper verloren geht.
Die Methode der Nutzerumfrage wurde nicht weiterverfolgt, da diese aufgrund zu wenigen Bedienern nicht zielführend war.
Die Methode der Nutzerumfrage wurde nicht weiterverfolgt, da diese auf Grund zu wenigen Bedienern nicht zielführend war.
Bei der Untersuchung der Datenbank, wurde festgestellt, dass die Struktur aktuell für die Anwendung optimal ist und
daher eine Restrukturierung keine Vorteile entstehen lässt. Die statische Webseite und die komplett Client basierte
Webseite wurde auf Grund von technischen Einschränkungen nicht weiterverfolgt.
@ -34,7 +34,7 @@ Performance festgestellt werden konnte.
Anders sieht es bei dem OpenJPA"=Cache aus, dieser hat direkten Einfluss auf die Performance der Ermittlung der Daten
und Bereitstellung der dazugehörigen Java"=Objekte. Anhand der vorgegeben Cache"=Größe kann das Potential der
Optimierung eingestellt werden. Dies bedeutet, soweit der Cache groß genug ist um alle notwendigen Objekte zu
speichern, sind die Verbesserung gut messbar. Ab dem Zeitpunkt, dass Objekte aus dem Cache entfernt werden müssen,
speichern, sind die Verbesserung gut sichtbar. Ab dem Zeitpunkt ab dem Objekte aus dem Cache entfernt werden müssen,
wird die Optimierung immer geringer.
Ein sehr ähnliches Verhalten konnte mit dem Ehcache festgestellt werden, nur dass bei diesem die Limitierungen höher
@ -51,13 +51,13 @@ durch die Umstellung der Ermittlung der unterlagerten Daten durch Hints eine Opt
bezweckt das die unterlagerten Daten nicht einzeln für jede Zeile ermittelt wurde, sondern alle Daten auf einmal
geladen werden und die Zuordnung der Datensätze im OpenJPA"=Framework durchgeführt wird.
Mit der übernahme der \textit{Materialized View} aus dem Wedekind"=Projekt konnte erstmalig ein gute Optimierung
Mit der Übernahme der \textit{Materialized View} aus dem Wedekind"=Projekt konnte erstmalig ein gute Optimierung
beobachtet werden. Dies ist auf die einfachere Abfrage, die Reduzierung der Abfrage an den Datenbankserver und das
die Objekte in eigenen Code erstellt werden und nicht durch das OpenJPA"=Framework. Hierbei konnte noch nachgewiesen
werden, dass das parsen der Json"=Daten, die die unterlagerten Objekte enthalten, den größten Teil der Zeit benötigen.
Auf Grund dessen wurde das parsen der Json"=Daten auf den Client verschoben, was zu einem noch besseren Ergebnis führte.
werden, dass das Parsen der Json"=Daten, die die unterlagerten Objekte enthalten, den größten Teil der Zeit benötigen.
Auf Grund dessen wurde das Parsen der Json"=Daten auf den Client verschoben, was zu einem noch besseren Ergebnis führte.
Für die Optimierung der Abfragen wurde die Hauptabfrage betrachtet. Bei dieser konnte anhand der visuell Darstellung
Für die Optimierung der Abfragen wurde die Hauptabfrage betrachtet. Bei dieser konnte anhand der visuellen Darstellung
das Problem gut identifiziert werden. Durch die Verwendung einer \textit{Common Table Expression} wurde die Anzahl
der Datensatze direkt am Anfang auf die angefragte Menge reduziert, wodurch die Anzahl der zu betrachteten Datensätze
für die weiteren Verlinkungen enorm reduziert wurden. Somit konnte der Datenbankserver bei diesen Verlinkung auf Indexe
@ -72,11 +72,11 @@ des Servers.
\label{sec:summary_and_outlook:outlook}
Die Untersuchungen zeigen, dass die Schichten über OpenJPA weitestgehend optimal umgesetzt sind, beziehungsweise wenig
Möglichkeiten für eine Optimierungen zu lassen. Einzig das parsen von Json ist in einem Webbrowser schneller als im
Server durchführbar. Auf diese Weise könnten zusätzlich die Ressourcen am Server reduziert werden, beziehungsweise mit
Möglichkeiten für eine Optimierungen zu lassen. Einzig das Parsen von Json ist in einem Webbrowser schneller als im
Server durchführbar. Auf diese Weise könnten zusätzlich die Ressourcen am Server reduziert werden beziehungsweise mit
gleichen Ressourcen mehr Anfragen als bisher beantwortet werden.
Die größten Optimierungspotentiale könne durch Umstellung der Abfragen und der Optimierung des ORM"=Mappers umgesetzt
Die größten Optimierungspotentiale können durch Umstellung der Abfragen und der Optimierung des ORM"=Mappers umgesetzt
werden. Bei den Umstellungen der Abfragen ist größte Stärke, wenn die Anzahl der Abfragen drastisch reduziert werden
können.

Binary file not shown.

View file

@ -25,7 +25,7 @@
\newcommand{\myUni}{FernUniversit\"at in Hagen\xspace}
\newcommand{\mySubjectArea}{Lehrgebiet Datenbanken und Informationssysteme\xspace}
\newcommand{\myLocation}{Höchstadt\xspace}
\newcommand{\myTime}{21. August 2024\xspace}
\newcommand{\myTime}{29. September 2024\xspace}
\newcommand{\myVersion}{version 1.0\xspace}
% ****************************************************************************************************
@ -61,3 +61,8 @@
% \definecolor{Black}{cmyk}{0, 0, 0, 0}
\definecolor{Black}{HTML}{221E1F} % Definition von https://en.wikibooks.org/wiki/LaTeX/Colors
\definecolor{Green}{HTML}{00A64f}
% Farbiges Markieren mit \texthl{...}
\usepackage{soul}
% ToDos erstellen mit \todo{...}
\usepackage{todonotes}

View file

@ -75,7 +75,7 @@
lineArrow/.style={arrows={-Latex[length=5pt 3 0]}},
every fit/.style={inner sep=.4em,draw}
]
\node (browser) [block] {WebBrowser};
\node (browser) [block] {Webbrowser};
\node (fitClient) [rounded corners,fit=(browser)] {};
\node [left] at (fitClient.west) {Client};

Binary file not shown.