bachelor-thesis/chapters/thesis/chapter02.tex

195 lines
14 KiB
TeX
Raw Permalink Normal View History

2024-07-31 22:12:53 +02:00
% !TeX root = ../../thesis.tex
\chapter{Grundlagen}
2024-03-21 21:05:04 +01:00
\label{ch:basics}
Da die Anwendung als Webseite umgesetzt ist, ist der zugehörige Client für den Benutzer ein Webbrowser. Dies bedeutet,
2024-09-10 00:46:35 +02:00
dass jeder Wechsel einer Seite oder eine Suchanfrage als Web"=Request an den Server geschickt wird. Solch ein Web"=Request
2024-03-31 22:25:29 +02:00
geht durch mehrere Schichten des Server"=System bis die Antwort an den Client zurückgesendet wird, wie in
2024-09-26 23:43:15 +02:00
\autoref{fig:webrequest} dargestellt.
2024-03-21 21:05:04 +01:00
2024-09-28 16:16:31 +02:00
Es wird ab hier 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
2024-09-28 13:33:24 +02:00
bereitgestellt wird und die neuesten Features unterstützt. Der \textit{Payara}"=Server ist aus dessen Quellcode entstanden
2024-09-29 13:27:02 +02:00
und ist für Produktivumgebungen gedacht, da dieser mit regelmäßigen Sicherheitsupdates versorgt wird. In diesem und dem folgenden Kapitel
2024-09-28 16:16:31 +02:00
wird für beide Anwendungen der Begriff \textit{GlassFish} verwendet.
2024-08-31 00:09:15 +02:00
2024-09-28 16:16:31 +02:00
Angefangen bei der Anfrage die über den Webbrowser an den Server gestellt wird und vom \textit{GlassFish}"=Server
2024-09-29 13:27:02 +02:00
empfangen wird. In diesem wird anhand des definierten Routings entschieden, an welchem \textit{Controller} im \textit{\ac{JSF}}
2024-09-03 23:48:35 +02:00
die Anfrage weitergeleitet und verarbeitet wird. In diesem wird die Darstellung der Webseite geladen und die Anfragen
für den darzustellenden Datenbestand abgeschickt.
2024-03-21 21:05:04 +01:00
2024-03-31 22:25:29 +02:00
Die Datenanfragen werden über die \textit{\ac{EJB}} an die \textit{\ac{JPA}} weitergeleitet.
2024-03-21 21:05:04 +01:00
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
2024-09-28 13:33:24 +02:00
\ac{DBMS} werden bei Bedarf im \textit{OpenJPA Cache} aktualisiert.
2024-03-21 21:05:04 +01:00
2024-09-27 20:58:59 +02:00
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 und die Bearbeitungszeit
zu verringern. Um Anfragen, die den Zugriff auf die Festplatte benötigen effizienter zu gestalten, bereiten die
2024-03-21 21:05:04 +01:00
\textit{Services} die Datenstrukturen auf.
2024-09-28 16:16:31 +02:00
\begin{figure}[H]
2024-03-21 21:05:04 +01:00
\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
2024-09-26 23:43:15 +02:00
\node (browser) [block] {Webbrowser};
2024-03-21 21:05:04 +01:00
\node (fitClient) [fit=(browser)] {};
\node [left] at (fitClient.west) {Client};
\node (JSF) [block,below of=browser,node distance=7em] {Java Server Faces};
\node (EJB) [block,below of=JSF] {Enterprise Java Beans};
2024-03-21 21:05:04 +01:00
\node (JPA) [block,below of=EJB] {Java Persistance API};
\node (openJPA) [block, below of=JPA] {OpenJPA Cache};
2024-09-28 16:16:31 +02:00
\node (fitGlassFish) [fit=(JSF) (EJB) (JPA) (openJPA)] {};
\node [left] at (fitGlassFish.west) {GlassFish};
2024-03-21 21:05:04 +01:00
\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};
2024-09-28 16:16:31 +02:00
\node (fitServer) [fit=(fitGlassFish) (fitPostgreSQL),inner xsep=5em] {};
2024-03-21 21:05:04 +01:00
\node [left] at (fitServer.west) {Server};
\draw[lineArrow] (browser)--(JSF);
\draw[lineArrow] (JSF)--(EJB);
2024-03-21 21:05:04 +01:00
\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}
2024-09-28 16:16:31 +02:00
\section{GlassFish - Enterprise Java Beans}
2024-03-21 21:05:04 +01:00
\label{sec:basics:ejb}
2024-03-31 22:25:29 +02:00
In den Java"=EE"=Anwendungen wird der \textit{Persistenzkontext} für die Anfragen vom \textit{Application"=Server}
2024-09-28 16:16:31 +02:00
bereitgestellt. Hierfür werden \textit{Application"=Server} wie \textit{GlassFish} genutzt, um die Verwendung eines Pools % nicht eher die EJB innerhalb von glassfish
2024-09-26 23:43:15 +02:00
von Datenbankverbindungen zu definieren \citep[68]{MüllerWehr2012}. Dadurch kann die Anzahl der Verbindungen geringer
2024-09-27 20:58:59 +02:00
gehalten werden als die Anzahl der Benutzer, die an der Anwendung arbeiten. Zusätzlich werden die Transaktionen über
2024-03-31 22:25:29 +02:00
\textit{\ac{SFSB}} gehandhabt, welche automatisch vor dem Aufruf erzeugt und danach wieder gelöscht
2024-03-21 21:05:04 +01:00
werden. Dies birgt allerdings den Nachteil, dass der \textit{Persistenzkontext} sehr groß werden kann, wenn viele
2024-09-26 23:43:15 +02:00
Entities in den \textit{Persistenzkontext} geladen werden. Da dies häufig zu Speicher"~ und damit Performance"=Problemen
2024-03-21 21:05:04 +01:00
\citep[79]{MüllerWehr2012} führen kann, muss hier darauf geachtet werden, nicht mehr benötigte Entities aus dem
\textit{Persistenzkontext} zu lösen.
2024-09-28 16:16:31 +02:00
\section{GlassFish - Java Persistance API}
2024-03-21 21:05:04 +01:00
\label{sec:basics:jpa}
2024-03-31 22:25:29 +02:00
Die \textit{\ac{JPA}} wird als First"=Level"=Cache in Java"=EE"=Anwendung verwendet, hier nehmen die
2024-09-26 23:43:15 +02:00
Objekte einen von vier Zuständen ein \citep[57]{MüllerWehr2012}. Im Zustand \texttt{Transient} sind die Objekte erzeugt,
2024-03-21 21:05:04 +01:00
aber noch nicht in den Cache überführt worden. Wenn diese in den Cache überführt worden sind, nehmen sie den Zustand
2024-09-26 23:43:15 +02:00
\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,
2024-09-27 20:58:59 +02:00
aber noch in der Datenbank verbleibt.
2024-03-21 21:05:04 +01:00
Eine Menge von Objekten wird als \textit{Persistenzkontext} bezeichnet. Solange die Objekte dem
2024-09-26 23:43:15 +02:00
\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
2024-03-21 21:05:04 +01:00
\textit{Automatic Dirty Checking} verwendet \citep[61]{MüllerWehr2012}.
2024-09-28 16:16:31 +02:00
\section{GlassFish - OpenJPA Cache}
2024-03-21 21:05:04 +01:00
\label{sec:basics:ojpac}
2024-09-28 13:33:24 +02:00
Zusätzlich kann im \textit{\ac{JPA}} ebenfalls noch der \textit{Second Level Cache} (L2-Cache) aktiviert werden. Dieser steht
2024-03-21 21:05:04 +01:00
jedem \textit{Persistenzkontext} zur Verfügung und kann dadurch die Anzahl der Datenbankzugriffe deutlich reduzieren,
2024-03-31 22:25:29 +02:00
was bei langsamen Datenbank"=Anbindungen zu hohen Performance"=Gewinnen führen kann \citep[171]{MüllerWehr2012}.
2024-09-27 20:58:59 +02:00
Zu Beachten ist, dass die Daten im \textit{Second Level Cache} explizit über die Änderungen informiert werden
müssen, um zu verhindern, dass bei einem nachfolgenden Aufruf veraltete Werte zurückgegeben werden. Ebenfalls benötigt ein Cache einen höheren Bedarf
2024-03-21 21:05:04 +01:00
an Arbeitsspeicher, in dem die Daten parallel zur Datenbank bereitgestellt werden, daher ist die Benutzung nur
2024-09-26 23:43:15 +02:00
problemlos bei Entities möglich, auf welche meist lesend zugegriffen wird.
2024-03-21 21:05:04 +01:00
2024-09-27 20:58:59 +02:00
In der OpenJPA"=Erweiterung für den L2-Cache wird in \textit{Objekt"=Cache} (in OpenJPA als \textit{DataCache}
2024-09-26 23:43:15 +02:00
bezeichnet) und \textit{Query"=Cache} unterschieden. Über die Funktionen \texttt{find()} und \texttt{refresh()} oder einer Query
2024-03-21 21:05:04 +01:00
werden die geladenen Entities in den Cache gebracht. Davon ausgenommen sind \textit{Large Result Sets} (Abfragen die
2024-03-31 22:25:29 +02:00
nicht alle Daten auf einmal laden), \texttt{Extent}"=Technologien und Queries, die einzelne Attribute von Entities
2024-03-21 21:05:04 +01:00
zurückliefern, aber nicht das Entity selbst. Hierbei kann genau gesteuert werden, welche Entity in den Cache abgelegt
2024-09-26 23:43:15 +02:00
werden und welche nicht. Ebenfalls kann auf Klassenbasis der zugehörige Cache definiert werden, um eine bessere
2024-03-21 21:05:04 +01:00
Last-Verteilung beim Zugriff zu ermöglichen \citep[314]{MüllerWehr2012}.
2024-09-26 23:43:15 +02:00
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
2024-03-21 21:05:04 +01:00
\citep[316]{MüllerWehr2012}.
2024-03-31 22:25:29 +02:00
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
2024-03-21 21:05:04 +01:00
die Einstellungen an den Entities angepasst werden \citep{IbmOpenJPACaching2023}.
\section{PostgreSQL - Memory Buffers}
\label{sec:basics:memorybuffers}
2024-03-31 22:25:29 +02:00
Die Speicherverwaltung des PostgreSQL"=Servers muss für Produktivsysteme angepasst werden \citep[34-38]{Eisentraut2013}.
2024-09-27 20:58:59 +02:00
Hierunter fallen die \texttt{shared\_buffers}, die bei circa 10 bis 25 Prozent des verfügbaren Arbeitsspeichers liegen
2024-03-21 21:05:04 +01:00
sollten. Mit dieser Einstellung wird das häufige Schreiben des Buffers durch Änderungen von Daten und Indexen auf die
Festplatte reduziert.
2024-09-27 20:58:59 +02:00
Die Einstellung \texttt{temp\_buffers} definiert, wie groß der Speicher für temporäre Tabellen pro
2024-03-21 21:05:04 +01:00
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.
2024-09-26 23:43:15 +02:00
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.
2024-03-21 21:05:04 +01:00
2024-09-26 23:43:15 +02:00
Die \texttt{maintenance\_work\_mem} wird bei Verwaltungsoperationen wie Änderungen und Erzeugungen von Datenbankobjekten
2024-03-21 21:05:04 +01:00
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
2024-03-31 22:25:29 +02:00
\citep[75]{Eisentraut2013}. Hierfür gibt es den \texttt{VACUUM}"=Befehl, welcher entweder per Hand oder automatisch durch
2024-03-21 21:05:04 +01:00
das Datenbanksystem ausgeführt werden soll. Für die automatische Ausführung kann der maximal verwendete Speicher über
2024-09-26 23:43:15 +02:00
die Einstellung \texttt{autovacuum\_work\_mem} gesondert definiert werden \citep{PostgresPro:Chap20.4:2023}.
2024-09-27 20:58:59 +02:00
Neben dem Aufräumen durch \texttt{VACUUM} sollten auch die Planerstatistiken mit \texttt{ANALYZE}
2024-03-21 21:05:04 +01:00
\citep[83]{Eisentraut2013} aktuell gehalten werden, damit die Anfragen durch den Planer richtig optimiert werden können.
2024-03-31 22:25:29 +02:00
Für beide Wartungsaufgaben gibt es den Autovacuum"=Dienst, dieser sollte aktiv und richtig konfiguriert sein.
2024-03-21 21:05:04 +01:00
Mit dem Tool \textit{pgFouine} \citep[155]{Eisentraut2013} können die Logs des PostgreSQL Server analysiert und auf
2024-09-10 00:46:35 +02:00
Probleme hin untersucht werden. Hiermit können sehr einfach die häufigsten beziehungsweise langsamsten Anfragen
ermittelt werden.
2024-03-21 21:05:04 +01:00
\section{PostgreSQL - Abfragen}
\label{sec:basics:queries}
2024-09-27 20:58:59 +02:00
Für weitere Optimierungen werden anschließend die Anfragen einzeln überprüft. Hierfür ist es sinnvoll, die
2024-03-21 21:05:04 +01:00
Ausführungspläne der Abfrage zu analysieren \citep[252]{Eisentraut2013}, die verschiedenen Plantypen und ihre Kosten zu
2024-09-27 20:58:59 +02:00
kennen sowie die angegeben Werte für die Plankosten zu verstehen \citep[24-30]{Dombrovskaya2021}.
2024-03-21 21:05:04 +01:00
Besonderes Augenmerk gilt dem Vergleichen des tatsächlich ausgeführten mit dem ursprünglichen Plan
2024-09-28 13:33:24 +02:00
\citep[254]{Eisentraut2013}. Eines der wichtigsten Kennzeichen hierbei ist, ob die Zeilenschätzung akkurat war.
2024-09-27 20:58:59 +02:00
Größere Abweichungen weisen häufig auf veraltete Statistiken hin.
2024-03-21 21:05:04 +01:00
Um die Abfragen selbst zu optimieren, gibt es ein Vorgehen über mehrere Schritte \citep[304-308]{Dombrovskaya2021}.
2024-09-26 23:43:15 +02:00
Zuerst wird unterschieden, ob es sich um eine \textit{Kurze} oder eine \textit{Lange} Abfrage handelt. Im Falle einer
2024-09-27 20:58:59 +02:00
\textit{Kurzen} Abfrage werden zuerst die Abfragekriterien überprüft. Sollte dies zu keiner Verbesserung führen,
2024-03-21 21:05:04 +01:00
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.
2024-09-27 20:58:59 +02:00
Des Weiteren können über das Modul \texttt{pg\_stat\_statements} Statistiken der Aufrufe, die an den Server gestellt
2024-03-21 21:05:04 +01:00
wurden, ermittelt werden \citep{PostgresF27:2023}. Hierbei können die am häufigsten Aufgerufenen und die Anfragen mit
2024-09-27 20:58:59 +02:00
der längsten Ausführungszeit ermittelt werden. Ohne das zusätzliche Modul können die Statistiken über die
2024-09-03 23:48:35 +02:00
Software \textit{pgBadger} erstellt werden. Dafür muss zusätzlich noch die Konfiguration des \textit{PostgreSQL}
angepasst werden.