Anpassung am Expose + Versand V3.1

This commit is contained in:
marcodn 2024-02-11 20:40:10 +01:00
parent f1e67c7514
commit c522eb4b14
2 changed files with 91 additions and 76 deletions

View file

@ -14,10 +14,9 @@ wenig erforscht wurde, soll sich dies nun Ändern. Die nationalen und internatio
zeigen eine starke Vernetzung in der europäischen Avantgarde. Aktuell sind lediglich 710 der 3200 bekannten Korrespondenzstücke
veröffentlicht worden.
Diese
beinhalteten substantiell das literarhistorische und kulturgeschichtliche Wissen über die Kultur zwischen 1880 und 1918,
indem das überlieferte Material zum einen transkribiert editiert und zum anderen editionswissenschaftlich kommentiert wurde.
Inhaltlich erschlossen zusätzliche Kommentare den historischen Kontext.
Diese beinhalten substantiell das literarhistorische und kulturgeschichtliche Wissen über die Kultur zwischen 1880
und 1918, indem das überlieferte Material zum einen transkribiert editiert und zum anderen editionswissenschaftlich
kommentiert wurde \citep{EffwFrankWedekind}.
Um jenes zu verändern entstand das Projekt >>Edition der Korrespondenz Frank Wedekind als Online-Volltextdatenbank<<
\citep{EffwFrankWedekind}, welches bei der EFFW angesiedelt ist und als Kooperationsprojekt an der Johannes
@ -49,15 +48,22 @@ Da die Anwendung als Webseite umgesetzt ist, ist der zugehörige Client für den
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},
@ -76,8 +82,10 @@ DBMS werden bei Bedarf im \textit{OpenJPA Cache} aktualisiert.
\node (fitGlassfish) [fit=(JSP) (EJB) (JPA) (openJPA)] {};
\node [left] at (fitGlassfish.west) {Glassfish};
\node (database) [block, below of=openJPA] {Database};
\node (fitPostgreSQL) [fit=(database)] {};
\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] {};
@ -87,75 +95,28 @@ DBMS werden bei Bedarf im \textit{OpenJPA Cache} aktualisiert.
\draw[lineArrow] (JSP)--(EJB);
\draw[lineArrow] (EJB)--(JPA);
\draw[lineArrow] (JPA)--(openJPA);
\draw[lineArrow] (openJPA)--(database);
\draw[lineArrow] (openJPA)--(memoryBuffers);
\draw[lineArrow] (memoryBuffers)--(database);
\draw[lineArrow] (services)|-(database);
\end{tikzpicture}
\caption{Ablauf einer Web-Anfrage}
\label{fig:webrequest}
\end{figure}
Für eine Optimierung werden die Schichten einzeln betrachtet. Ein sinnvolles Vorgehen ist hierbei von
unten nach oben vorzugehen. Dies bedeutet man beginnt mit dem Datenbankmanagementsystem, was in diesem Fall ein
PostgreSQL-Server ist.
\subsection{Glassfisch - Enterprise Java Beans}
\subsection{PostgreSQL}
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.
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.
\subsection{Glassfish - Java Persinstance API}
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.
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.
Für weitere Optimierungen sollen 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.
\subsection{Glassfish}
% MÜllerWehr2012
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
@ -168,15 +129,7 @@ Eine Menge von Objekten wird als \textit{Persistenzkontext} bezeichnet. Solange
ü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}.
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.
\subsection{Glassfish - OpenJPA Cache}
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,
@ -204,6 +157,68 @@ Um zu prüfen, ob die Einstellungen sinnvoll gesetzt sind, kann in OpenJPA eine
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}.
\subsection{PostgreSQL - Memory Buffers}
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.
\subsection{PostgreSQL - 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.
\subsection{PostgreSQL - Abfragen}
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.
\section{Vorgehen bei der Umsetzung}
Durch eine Umfrage der Bediener und Entwickler, einer Per\-for\-mance-Mes\-sung in der Webseite und den Statistiken im

Binary file not shown.