diff --git a/chapters/expose/chapter01.tex b/chapters/expose/chapter01.tex index 4dcb0f7..8299f46 100644 --- a/chapters/expose/chapter01.tex +++ b/chapters/expose/chapter01.tex @@ -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 diff --git a/expose.pdf b/expose.pdf index feec231..137323d 100644 Binary files a/expose.pdf and b/expose.pdf differ