diff --git a/chapters/thesis/chapter01.tex b/chapters/thesis/chapter01.tex index c9842c3..5aa0ff6 100644 --- a/chapters/thesis/chapter01.tex +++ b/chapters/thesis/chapter01.tex @@ -2,3 +2,43 @@ \chapter{Einleitung} \label{ch:intro} +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 +um eine längere Bearbeitung anzuzeigen. + +\section{Motivation} +\label{sec:intro:motivation} + +Lorem Ipsum + +\section{Ziel der Arbeit} +\label{sec:intro:goal} + +Die aktuelle Umsetzung beinhaltet die bisher definierten Anforderungen vollständig, darunter fallen die +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 Technologien angedacht. + +\section{Gliederung} +\label{sec:intro:structure} + +Zu Begin der Arbeit werden im Kapitel \ref{ch:basics} die Struktur und der grundsätzliche Aufbau der Anwendung +erklärt. Hierbei wird aufgezeigt auf welche Probleme auftreten können und wie diese zu überprüfen sind. +Nachfolgenden wird im Kapitel \ref{ch:concept} die Konzepte vorgestellt und aus verschiedenen Blickwinkel +betrachtet. Hierbei wird versucht .... +Bei Performance-Untersuchung in Kapitel \ref{ch:performance-checking} werden nun die Konzepte angewandt, um +die Probleme zu identifizieren. +Danach werden die Untersuchungen ausgewertet und die Performance-Probleme identifiziert. Hierbei ist nun ein +gesondertes Vorgehen je erkannten Problem durchzuführen, um diese zu beheben oder mindestens in einen akzeptablen +Rahmen zu verbessern. +Nach der Optimierung kommt nun die Evaluierung im Kapitel \ref{ch:evaluation} um zu überprüfen ob die Anpassungen +die gewünschten Verbesserung in der Performance gebracht haben. +Zum Abschluss wird explizit die Anpassungen dargestellt, die zu einer Verbesserung geführt haben und wie diese +entsprechend umgesetzt werden müssen. Zusätzliche wird beschrieben wie ein weiteres Vorgehen durchgeführt werden +kann. diff --git a/chapters/thesis/chapter02.tex b/chapters/thesis/chapter02.tex index e8e7e6e..9571097 100644 --- a/chapters/thesis/chapter02.tex +++ b/chapters/thesis/chapter02.tex @@ -1,3 +1,186 @@ \chapter{Grundlagen} -\label{ch:intro} \ No newline at end of file +\label{ch:basics} + +Da die Anwendung als Webseite umgesetzt ist, ist der zugehörige Client für den Benutzer ein Webbrowser. Dies bedeutet, +das 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. + +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 welche \textit{Java Server Page} die +Anfrage weitergeleitet und verarbeitet wird. In dieser wird die Darstellung der Webseite geladen und die Anfragen für +den darzustellenden Datenbestand abgeschickt. + +Die Datenanfragen werden über die \textit{Enterprise Java Beans} an die \textit{Java Persistance API} weitergeleitet. +Hier wird nun geprüft, ob die Daten aus dem \textit{OpenJPA Cache} direkt ermittelt werden können, oder ob die Abfrage +an das unterlagerte Datenbankmanagementsystem \textit{PostgreSQL} weitergeleitet werden muss. Die ermittelten Daten vom +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 +zu verringern. Um Anfragen die den Zugriff auf die Festplatte benötigen effizienter zu gestalten, bereiten die +\textit{Services} die Datenstrukturen auf. + +\begin{figure}[h!] + \begin{tikzpicture}[node distance=5em, + block/.style={rectangle, rounded corners,minimum width=3cm,minimum height=1cm,text centered, draw=black,fill=green!30}, + lineArrow/.style={arrows={-Latex[length=5pt 3 0]}}, + every fit/.style={inner sep=1em,draw} + ] + %https://docs.oracle.com/javaee/6/tutorial/doc/bnacj.html + \node (browser) [block] {WebBrowser}; + \node (fitClient) [fit=(browser)] {}; + \node [left] at (fitClient.west) {Client}; + + \node (JSP) [block,below of=browser,node distance=7em] {Java Server Pages}; + \node (EJB) [block,below of=JSP] {Enterprise Java Beans}; + \node (JPA) [block,below of=EJB] {Java Persistance API}; + \node (openJPA) [block, below of=JPA] {OpenJPA Cache}; + \node (fitGlassfish) [fit=(JSP) (EJB) (JPA) (openJPA)] {}; + \node [left] at (fitGlassfish.west) {Glassfish}; + + \node (memoryBuffers) [block, below of=openJPA] {Memory Buffers}; + \node (services) [block, right of=memoryBuffers, xshift=2cm] {Services}; + \node (database) [block, below of=memoryBuffers] {Database}; + \node (fitPostgreSQL) [fit=(memoryBuffers) (services) (database)] {}; + \node [left] at (fitPostgreSQL.west) {PostgreSQL}; + + \node (fitServer) [fit=(fitGlassfish) (fitPostgreSQL),inner xsep=5em] {}; + \node [left] at (fitServer.west) {Server}; + + \draw[lineArrow] (browser)--(JSP); + \draw[lineArrow] (JSP)--(EJB); + \draw[lineArrow] (EJB)--(JPA); + \draw[lineArrow] (JPA)--(openJPA); + \draw[lineArrow] (openJPA)--(memoryBuffers); + \draw[lineArrow] (memoryBuffers)--(database); + \draw[lineArrow] (services)|-(database); + \end{tikzpicture} + \caption{Ablauf einer Web-Anfrage} + \label{fig:webrequest} +\end{figure} + +\section{Glassfisch - Enterprise Java Beans} +\label{sec:basics:ejb} + +In den Java-EE-An\-wen\-dung\-en 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 +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 +\citep[79]{MüllerWehr2012} führen kann, muss hier darauf geachtet werden, nicht mehr benötigte Entities aus dem +\textit{Persistenzkontext} zu lösen. + +\section{Glassfish - Java Persinstance API} +\label{sec:basics:jpa} + +Die \textit{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 +\textit{Gelöscht} an. \textit{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{Automatic Dirty Checking} verwendet \citep[61]{MüllerWehr2012}. + +\section{Glassfish - OpenJPA Cache} +\label{sec:basics:ojpac} + +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 +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. + +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 +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 +Last-Verteilung beim Zugriff zu ermöglichen \citep[314]{MüllerWehr2012}. + +Im \textit{Query-Cache} werden die Abfragen bzw. 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 +\citep[316]{MüllerWehr2012}. + +Um zu prüfen, ob die Einstellungen sinnvoll gesetzt sind, kann in OpenJPA eine Cache-Statistik abgefragt werden. Mit +dieser kann die Anzahl der Lese- und Schreibzugriffe im Cache überprüft werden, entsprechend dieser Auswertung sollten +die Einstellungen an den Entities angepasst werden \citep{IbmOpenJPACaching2023}. + +\section{PostgreSQL - Memory Buffers} +\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 ca. 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 +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. + +Die \textit{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. + +\section{PostgreSQL - Services} +\label{sec:basics:services} + +Die Wartung des Datenbanksystems ist eine der wichtigsten Aufgaben und sollte regelmäßig +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}. +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. + +Mit dem Tool \textit{pgFouine} \citep[155]{Eisentraut2013} können die Logs des PostgreSQL Server analysiert und auf +Probleme hin untersucht werden. Hiermit können sehr einfach die häufigsten bzw. langsamsten Anfragen ermittelt werden. + +\section{PostgreSQL - Abfragen} +\label{sec:basics:queries} + +Für weitere Optimierungen werden anschließend die Anfragen einzeln überprüft. Hierfür ist es sinnvoll die +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. + +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 +\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. +Bei einer \textit{Langen} Abfrage soll überprüft werden, ob es sinnvoll ist, das Ergebnis in einer Tabelle zu +speichern und bei Änderungen zu aktualisieren. Wenn dies nicht möglich ist, sollten die folgenden Schritte durchgeführt +werden. Zuerst wird der restriktivste Join gesucht und überprüft, ob dieser als Erstes ausgeführt wird. Anschließend fügt +man weitere Joins hinzu und prüft die Ausführungszeit und die Abfragepläne. Als Nächstes wird sich vergewissert, ob +große Tabellen nicht mehrfach durchsucht worden sind. Bei Gruppierungen ist noch zu prüfen, ob diese früher durchgeführt +werden können, um die Abfragemenge zu verringern. + +Bei \textit{Langen} Abfragen ist die Abhandlung >>Optimizing Iceberg Queries with Complex Joins<< +\citep{10.1145/3035918.3064053} ein zusätzlicher Ratgeber, um die Performance zu steigern. + +Des Weiteren können über das Modul \texttt{pg\_stat\_statements} Statistiken der Aufrufe die an den Server gestellt +wurden, ermittelt werden \citep{PostgresF27:2023}. Hierbei können die am häufigsten Aufgerufenen und die Anfragen mit +der längsten Ausführungszeit ermittelt werden. + +% \ No newline at end of file diff --git a/chapters/thesis/chapter03.tex b/chapters/thesis/chapter03.tex index a056252..00ca368 100644 --- a/chapters/thesis/chapter03.tex +++ b/chapters/thesis/chapter03.tex @@ -4,6 +4,11 @@ \section{Aufbau der Umfrage} +% erste Frage sollte unterscheiden ob Bearbeiter oder Benutzer ist +% an welchen Aktion kommt es zu längeren Wartezeiten +% wie häufig sind die langen Wartezeiten +% treten sie sporadisch oder immer mit gleichen Muster (z.B. immer Nachmittags, immer bei der gleichen Abfolge an Aktionen) auf + \section{Allgemeine Betrachtung des Systems} \section{Das Vorgehen der Optimierung} diff --git a/frontbackmatter/thesis/Acronyms.tex b/frontbackmatter/thesis/Acronyms.tex new file mode 100644 index 0000000..cf53322 --- /dev/null +++ b/frontbackmatter/thesis/Acronyms.tex @@ -0,0 +1,20 @@ +%******************************************************* +% Acronyms +%******************************************************* +\automark[section]{chapter} +\renewcommand{\chaptermark}[1]{\markboth{\spacedlowsmallcaps{#1}}{\spacedlowsmallcaps{#1}}} +\renewcommand{\sectionmark}[1]{\markright{\thesection\enspace\spacedlowsmallcaps{#1}}} +\refstepcounter{dummy} +\pdfbookmark[0]{Abk\"{u}rzungsverzeichnis}{abkuerzungsverzeichnis} +\markboth{\spacedlowsmallcaps{Abk\"{u}rzungsverzeichnis}}{\spacedlowsmallcaps{Abk\"{u}rzungsverzeichnis}} +\chapter*{Abk\"{u}rzungsverzeichnis} + +% Insert your acronyms here +\begin{acronym}[UML] + \acro{SFSB}{Stateful Session-Bean} + \acro{JPA}{Java Persistence API} + \acro{API}{Application Programming Interface} + \acro{UML}{Unified Modeling Language} +\end{acronym} + +\cleardoublepage diff --git a/thesis.pdf b/thesis.pdf index 43244a0..e1e2992 100644 Binary files a/thesis.pdf and b/thesis.pdf differ diff --git a/thesis.tex b/thesis.tex index d6869b0..ac8bb9c 100644 --- a/thesis.tex +++ b/thesis.tex @@ -65,7 +65,7 @@ \cleardoublepage\include{frontbackmatter/Figures} %\cleardoublepage\include{frontbackmatter/Tables} \cleardoublepage\include{frontbackmatter/Listings} -%\cleardoublepage\include{frontbackmatter/Acronyms} +\cleardoublepage\include{frontbackmatter/thesis/Acronyms} %************************************************************************* % Mainmatter %*************************************************************************