2024-07-31 22:12:53 +02:00
|
|
|
% !TeX root = ../../thesis.tex
|
2024-01-27 13:37:35 +01:00
|
|
|
|
2024-08-19 21:00:19 +02:00
|
|
|
\chapter{Performance-Untersuchung der Anwendung}
|
|
|
|
\label{ch:performance-investigation-application}
|
2024-01-27 13:37:35 +01:00
|
|
|
|
2024-09-10 00:46:35 +02:00
|
|
|
Nun werden die unterschiedlichen Schichten betrachtet und möglichen Performance"=Verbesserungen untersucht und deren
|
2024-08-19 21:00:19 +02:00
|
|
|
Vor"= und Nachteile herausgearbeitet.
|
2024-06-02 15:43:11 +02:00
|
|
|
|
2024-08-19 21:00:19 +02:00
|
|
|
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.
|
2024-01-27 13:37:35 +01:00
|
|
|
|
2024-08-19 21:00:19 +02:00
|
|
|
Zur ersten Untersuchung und der Bestimmung der Basis-Linie, wurde das Script ohne eine Änderung an dem Code und der
|
2024-09-10 00:46:35 +02:00
|
|
|
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
|
2024-08-19 21:00:19 +02:00
|
|
|
Server nicht mehr reagiert und im Log ist ein OutOfMemoryError protokolliert worden.
|
2024-01-27 13:37:35 +01:00
|
|
|
|
2024-08-19 21:00:19 +02:00
|
|
|
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
|
2024-09-10 00:46:35 +02:00
|
|
|
Auswertung zeigte, dass der Server mit circa 1500 MB RSS Nutzung an seine Grenzen stößt. Diese Grenzen wurde durch die
|
2024-08-19 21:00:19 +02:00
|
|
|
Konfigurationsänderung im Payara-Server von \texttt{-Xmx512m} auf \texttt{-Xmx4096m} nach oben verschoben. Nun werden
|
2024-09-10 00:46:35 +02:00
|
|
|
circa 60 Aufrufe des Scripts benötigt, damit der Server nicht mehr reagiert. Hierbei wird aber kein OutOfMemoryError
|
|
|
|
in der Log-Datei protokolliert und der Server verwendet nun circa 4700 MB RSS. Bei allen Tests war noch mehr als die
|
2024-09-01 23:06:28 +02:00
|
|
|
Hälfte des verfügbaren Arbeitsspeichers des Computers ungenutzt.
|
2024-01-27 13:37:35 +01:00
|
|
|
|
2024-09-05 00:02:53 +02:00
|
|
|
Mit der Konfiguration \texttt{-Xmx} wird der maximal verwendbare Heap"=Speicher in der \ac{JVM} definiert.
|
2024-09-01 23:06:28 +02:00
|
|
|
Dies zeigt direkt, dass es ein Problem in der Freigabe der Objekte gibt, da dass erhöhen des verwendbaren Arbeitsspeicher
|
2024-08-19 21:00:19 +02:00
|
|
|
das Problem nicht löst, sondern nur verschiebt.
|
2024-01-27 13:37:35 +01:00
|
|
|
|
2024-09-05 00:02:53 +02:00
|
|
|
Für alle nachfolgenden Messungen wird das Skript \ref{ap:calling_script} verwendet, welches die einzelnen Aufrufe
|
|
|
|
steuert. Die Ergebnisse werden in eine Tabelle überführt, wie in der Tabelle \ref{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
|
|
|
|
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.
|
|
|
|
|
2024-08-19 21:00:19 +02:00
|
|
|
Als Grundlage für die Vergleiche wurden eine Messung durchgeführt, bei der alle Caches deaktiviert wurden und keine
|
|
|
|
Änderung am Code vorgenommen wurde. Das Ergebnis dieser Messung ist in \ref{tbl:measure-without-cache} zu finden. Diese
|
|
|
|
zeigen auch direkt ein erwartetes 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
|
|
|
|
gehalten werden sollten.
|
2024-01-27 13:37:35 +01:00
|
|
|
|
2024-08-21 23:44:10 +02:00
|
|
|
\mytodos{Diese Tabelle vielleicht auch einfach komplett streichen, da der Datenbestand anders ist, und das wichtigste
|
|
|
|
die Zeit der SQL-Abfragen nicht sichtbar ist}
|
2024-08-19 21:00:19 +02:00
|
|
|
|
|
|
|
\begin{table}[h!]
|
|
|
|
\centering
|
|
|
|
\begin{tabular}{|r|r|r|r|r|r|r|r|}
|
|
|
|
\hline
|
2024-08-21 23:44:10 +02:00
|
|
|
& \multicolumn{3}{c}{Aufrufzeit (ms)} & & \multicolumn{3}{|c|}{RSS (MB)} \\
|
2024-08-19 21:00:19 +02:00
|
|
|
\hline
|
|
|
|
\# & min & avg & max & Queries & davor & danach & diff \\
|
|
|
|
\hline
|
|
|
|
1 & 395 & 578 & 1312 & 12237 & 747.15 & 924.88 & 177.73 \\
|
|
|
|
2 & 353 & 375 & 464 & 12080 & 924.51 & 1027.75 & 103,24 \\
|
|
|
|
3 & 286 & 345 & 535 & 12080 & 1018.21 & 1145.36 & 127.15 \\
|
|
|
|
4 & 291 & 307 & 340 & 12080 & 1129.91 & 1239.75 & 109,84 \\
|
|
|
|
\hline
|
|
|
|
\end{tabular}
|
|
|
|
\caption{Messung ohne Caches}
|
|
|
|
\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
|
|
|
|
beginnen. Hierbei ist aufgefallen, dass fast immer 62 Abfragen zur Startup-Phase dazugehört haben, unabhängig von den
|
2024-09-10 00:46:35 +02:00
|
|
|
konfigurierten Cache Einstellungen. Einige dieser Abfragen sind durch das Erstellen der Materialisierten Sichten
|
|
|
|
\textbf{searchreference} und \textit{searchfulltext} erklärbar. Zusätzlich ist noch ein zyklischer Dienst
|
|
|
|
\textit{SearchEntityService} vorhanden, der zum Start und alle sechs Stunden den Datenbestand für die Suche aufbereitet
|
|
|
|
und entsprechend einige Abfragen an die Datenbank absetzt. Da weder die Sichten noch der Dienst für die Dokumentenliste
|
|
|
|
benötigt werden, wurde der Dienst und das Erstellen im Code für die weiteren Tests deaktiviert.
|
2024-08-19 21:00:19 +02:00
|
|
|
|
|
|
|
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 Anhang
|
|
|
|
\ref{ap:docker_config} beschrieben.
|
|
|
|
|
|
|
|
Mit dem neuen Aufbau ergeben sich nun neue Messungen. Für den Speicherbedarf wird nun nicht mehr der benutzte
|
2024-09-01 23:06:28 +02:00
|
|
|
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.
|
|
|
|
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 CoAutoren, die Ermittlung der Faksimile, sowie die Ermittlung
|
|
|
|
der Anzahl aller vorhandenen Dokumente.
|
2024-08-19 21:00:19 +02:00
|
|
|
|
2024-09-08 00:33:09 +02:00
|
|
|
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.
|
|
|
|
|
|
|
|
\begin{table}[h]
|
2024-08-19 21:00:19 +02:00
|
|
|
\centering
|
2024-09-08 00:33:09 +02:00
|
|
|
\resizebox{\textwidth}{!}{
|
|
|
|
\begin{tabular}{|r|r|r|r|r|r|r|r|r|r|r|r|r|r|r|}
|
2024-08-19 21:00:19 +02:00
|
|
|
\hline
|
2024-09-08 00:33:09 +02:00
|
|
|
& \multicolumn{3}{c|}{Aufrufzeit (ms)} & \multicolumn{2}{c|}{Queries (ms)} & \multicolumn{3}{c|}{Memory (MB)} & \multicolumn{3}{c|}{Render (ms)} & \multicolumn{3}{c|}{DB-load (ms)} \\
|
2024-08-19 21:00:19 +02:00
|
|
|
\hline
|
2024-09-08 00:33:09 +02:00
|
|
|
\# & min & avg & max & \#"=avg & avg & start & stop & diff & min & avg & max & min & avg & max \\
|
2024-08-19 21:00:19 +02:00
|
|
|
\hline
|
2024-09-08 00:33:09 +02:00
|
|
|
1 & 451 & 682 & 1931 & 1223.0 & 30.3 & 931.3 & 986.1 & 54.8 & 440 & 666 & 1859 & 290 & 399 & 710 \\ % 12230 - 303 ms (135+ 79+ 39+ 22+17+11) (#2-6,8)
|
|
|
|
2 & 341 & 389 & 478 & 1208.0 & 31.2 & 986.5 & 1159.0 & 172.5 & 331 & 378 & 468 & 235 & 282 & 367 \\ % 24310 - 615 ms (270+156+ 78+ 56+34+21) (#2-7)
|
|
|
|
3 & 299 & 407 & 682 & 1208.0 & 33.5 & 1163.0 & 1273.0 & 110.0 & 290 & 398 & 672 & 207 & 307 & 579 \\ % 36390 - 950 ms (406+256+118+ 79+55+36) (#2-7)
|
|
|
|
4 & 278 & 359 & 424 & 1208.0 & 33.7 & 1272.0 & 1465.0 & 193.0 & 270 & 351 & 415 & 198 & 269 & 342 \\ % 48470 - 1287 ms (564+334+167+105+72+45) (#2-7)
|
|
|
|
5 & 264 & 317 & 356 & 1208.0 & 32.9 & 1467.0 & 1574.0 & 107.0 & 256 & 309 & 348 & 184 & 235 & 276 \\ % 60560 - 1616 ms (699+428+210+128+92+59) (#2-7)
|
2024-08-19 21:00:19 +02:00
|
|
|
\hline
|
|
|
|
\end{tabular}
|
2024-09-08 00:33:09 +02:00
|
|
|
}
|
2024-08-19 21:00:19 +02:00
|
|
|
\caption{Messung ohne Caches im Docker}
|
|
|
|
\label{tbl:measure-without-cache-docker}
|
|
|
|
\end{table}
|
|
|
|
|
2024-09-01 23:06:28 +02:00
|
|
|
\section{Umgestalten der Datenbanktabellen}
|
|
|
|
\label{sec:performance-investigation-application:new-table}
|
|
|
|
|
|
|
|
Hierfür wurde die aktuelle Datenstruktur untersucht um zu prüfen, ob eine Umgestaltung der Tabelle einen Verbesserung
|
|
|
|
bringen würden. Die typische Optimierung ist die Normalisierung der Tabellenstruktur. Die Tabellenstruktur ist aktuell
|
2024-09-10 00:46:35 +02:00
|
|
|
schon normalisiert, daher kann hier nichts weiter optimiert werden.
|
2024-09-01 23:06:28 +02:00
|
|
|
|
2024-09-10 00:46:35 +02:00
|
|
|
Eine weitere Optimierungsstrategie besteht in der Denormalisierung, um sich die Verknüpfungen der Tabellen zu sparen.
|
2024-09-01 23:06:28 +02:00
|
|
|
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. Oder man muss die Duplikate auf der Serverseite
|
|
|
|
zusammenführen.
|
2024-09-10 00:46:35 +02:00
|
|
|
\mytodos{Gefällt der Verena nicht so}
|
2024-09-01 23:06:28 +02:00
|
|
|
|
2024-08-19 21:00:19 +02:00
|
|
|
\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
|
|
|
|
\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
|
2024-09-01 23:06:28 +02:00
|
|
|
Objekte aus dem Cache entfernt und in eine SoftReferenceMap übertragen. Bei der Berechnung der Anzahl der Element werden
|
|
|
|
angeheftete Objekte nicht beachtet.
|
|
|
|
|
|
|
|
Die Anzahl der Soft References kann ebenfalls über eine Einstellung gesteuert werden. Hierfür wird die Anzahl der
|
|
|
|
Elemente über \textit{SoftReferenceSize} gesetzt, dessen Wert im Standard auf \textit{unbegrenzt} steht. Mit dem Wert
|
|
|
|
\textit{0} werden die Soft Referencen komplett deaktiviert. Über die Attribute an den Entitätsklassen, können diese
|
|
|
|
Referenzen ebenfalls gesteuert werden, hierzu muss eine Überwachungszeit angegeben werden. Diese Zeit gibt in ms an,
|
|
|
|
wie lange ein Objekt gültig bleibt. Mit dem Wert \textit{-1} wird das Objekt nie ungültig, was ebenfalls der
|
|
|
|
Standardwert ist.
|
2024-08-19 21:00:19 +02:00
|
|
|
|
|
|
|
Zuerst wird mit aktivierten Cache mit einer Cache-Größe von 1000 Elemente getestet. Wie in \ref{tbl:measure-ojpa-active}
|
2024-08-21 23:44:10 +02:00
|
|
|
zu sehen, dauert auch hier der erste Aufruf minimal länger als ohne aktiviertem Cache. Alle Nachfolgenden Aufrufe
|
2024-09-01 23:06:28 +02:00
|
|
|
wiederrum sind um 100ms schneller in der Verarbeitung. Auch bei der Anzahl der Anfragen an die Datenbank kann der
|
|
|
|
Rückgang der Anfragen sehr gut gesehen werden. Aktuell kann die Verringerung des wachsenden Speicherbedarfs nur nicht
|
|
|
|
erklärt werden.
|
2024-08-19 21:00:19 +02:00
|
|
|
|
|
|
|
\begin{table}[h!]
|
|
|
|
\centering
|
2024-09-08 00:33:09 +02:00
|
|
|
\resizebox{\textwidth}{!}{
|
|
|
|
\begin{tabular}{|r|r|r|r|r|r|r|r|r|r|r|r|r|r|r|}
|
2024-08-19 21:00:19 +02:00
|
|
|
\hline
|
2024-09-08 00:33:09 +02:00
|
|
|
& \multicolumn{3}{c|}{Aufrufzeit (ms)} & \multicolumn{2}{c|}{Queries (ms)} & \multicolumn{3}{c|}{Memory (MB)} & \multicolumn{3}{c|}{Render (ms)} & \multicolumn{3}{c|}{DB-load (ms)} \\
|
2024-08-19 21:00:19 +02:00
|
|
|
\hline
|
2024-09-08 00:33:09 +02:00
|
|
|
\# & min & avg & max & \#"=avg & avg & start & stop & diff & min & avg & max & min & avg & max \\
|
2024-08-19 21:00:19 +02:00
|
|
|
\hline
|
2024-09-08 00:33:09 +02:00
|
|
|
1 & 291 & 611 & 2347 & 730.2 & 28.8 & 852.7 & 891.9 & 39.2 & 282 & 595 & 2286 & 172 & 284 & 770 \\ % 7302 - 288 ms (145+ 42+ 40+ 24+18+ 8+ 7+ 4) (#2-8,12)
|
|
|
|
2 & 278 & 319 & 422 & 667.3 & 25.8 & 892.7 & 1010.0 & 117.3 & 266 & 309 & 411 & 173 & 195 & 220 \\ % 13975 - 546 ms (282+ 81+ 70+ 47+33+14+11+ 8) (#2-9)
|
|
|
|
3 & 229 & 281 & 329 & 680.6 & 27.6 & 1011.0 & 1067.0 & 56.0 & 220 & 271 & 313 & 134 & 180 & 222 \\ % 20781 - 822 ms (430+120+ 99+ 77+49+20+16+11) (#2-9)
|
|
|
|
4 & 222 & 280 & 321 & 671.3 & 27.6 & 1067.0 & 1122.0 & 55.0 & 213 & 271 & 310 & 131 & 189 & 238 \\ % 27494 - 1098 ms (569+160+137+ 99+68+26+22+17) (#2-9)
|
|
|
|
5 & 206 & 272 & 388 & 683.6 & 27.6 & 1122.0 & 1219.0 & 97.0 & 199 & 264 & 380 & 122 & 175 & 291 \\ % 34330 - 1374 ms (704+202+171+128+86+34+27+22) (#2-9)
|
2024-08-19 21:00:19 +02:00
|
|
|
\hline
|
|
|
|
\end{tabular}
|
2024-09-08 00:33:09 +02:00
|
|
|
}
|
2024-08-19 21:00:19 +02:00
|
|
|
\caption{Messung mit OpenJPA-Cache und Größe auf 1000}
|
|
|
|
\label{tbl:measure-ojpa-active}
|
|
|
|
\end{table}
|
|
|
|
|
2024-08-21 00:30:20 +02:00
|
|
|
Bei einer erhöhten Cache-Größe, von 1000 auf 10000, zeigt sich auf den ersten Blick ein noch besseres Bild ab, wie in
|
2024-08-19 21:00:19 +02:00
|
|
|
\ref{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 um 100 ms
|
|
|
|
beschleunigt werden konnte.
|
|
|
|
|
|
|
|
\begin{table}[h!]
|
|
|
|
\centering
|
2024-09-08 00:33:09 +02:00
|
|
|
\resizebox{\textwidth}{!}{
|
|
|
|
\begin{tabular}{|r|r|r|r|r|r|r|r|r|r|r|r|r|r|r|}
|
2024-08-19 21:00:19 +02:00
|
|
|
\hline
|
2024-09-08 00:33:09 +02:00
|
|
|
& \multicolumn{3}{c|}{Aufrufzeit (ms)} & \multicolumn{2}{c|}{Queries (ms)} & \multicolumn{3}{c|}{Memory (MB)} & \multicolumn{3}{c|}{Render (ms)} & \multicolumn{3}{c|}{DB-load (ms)} \\
|
2024-08-19 21:00:19 +02:00
|
|
|
\hline
|
2024-09-08 00:33:09 +02:00
|
|
|
\# & min & avg & max & \#"=avg & avg & start & stop & diff & min & avg & max & min & avg & max \\
|
2024-08-19 21:00:19 +02:00
|
|
|
\hline
|
2024-09-08 00:33:09 +02:00
|
|
|
1 & 151 & 368 & 1904 & 141.2 & 20.8 & 906.3 & 936.8 & 30.5 & 164 & 404 & 2232 & 39 & 124 & 847 \\ % 1412 - 208 ms (133+ 40+ 23+9+2+1) (#2,4-6,10,12)
|
|
|
|
2 & 133 & 143 & 159 & 6.0 & 20.5 & 935.7 & 939.3 & 3.6 & 121 & 136 & 146 & 32 & 36 & 44 \\ % 1472 - 413 ms (274+ 80+ 47+9+2+1) (#2-3,5,6,10,12)
|
|
|
|
3 & 120 & 126 & 132 & 6.0 & 19.9 & 939.4 & 942.7 & 3.3 & 116 & 136 & 256 & 32 & 47 & 167 \\ % 1532 - 612 ms (412+119+ 69+9+2+1) (#2,3,5,6,10,12)
|
|
|
|
4 & 120 & 124 & 128 & 6.0 & 21.4 & 944.3 & 945.4 & 1.1 & 105 & 113 & 125 & 30 & 32 & 39 \\ % 1592 - 826 ms (550+168+ 96+9+2+1) (#2-4,6,10,12)
|
|
|
|
5 & 109 & 114 & 131 & 6.0 & 19.7 & 945.5 & 946.7 & 1.2 & 101 & 107 & 112 & 30 & 32 & 35 \\ % 1652 - 1023 ms (683+209+119+9+2+1) (#2-4,6,10,12)
|
2024-08-19 21:00:19 +02:00
|
|
|
\hline
|
|
|
|
\end{tabular}
|
2024-09-08 00:33:09 +02:00
|
|
|
}
|
2024-08-19 21:00:19 +02:00
|
|
|
\caption{Messung mit OpenJPA-Cache und Größe auf 10000}
|
|
|
|
\label{tbl:measure-ojpa-active-bigger}
|
|
|
|
\end{table}
|
|
|
|
|
2024-09-08 00:33:09 +02:00
|
|
|
Bei dem deaktivieren der \textit{SoftReference} und dem kleineren Cache zeigt sich keine große Differenz. Somit scheint
|
|
|
|
die \textit{SoftReference} nicht das Problem für den steigenden Arbeitsspeicher zu sein, wie in Tabelle
|
|
|
|
\ref{tbl:measure-ojpa-active-bigger-no-softref} zu sehen.
|
|
|
|
|
|
|
|
% document, documentaddresseeperson, first/last, documentcoauthorperson, count und documentfacsimile
|
|
|
|
\begin{table}[h!]
|
|
|
|
\centering
|
|
|
|
\resizebox{\textwidth}{!}{
|
|
|
|
\begin{tabular}{|r|r|r|r|r|r|r|r|r|r|r|r|r|r|r|}
|
|
|
|
\hline
|
|
|
|
& \multicolumn{3}{c|}{Aufrufzeit (ms)} & \multicolumn{2}{c|}{Queries (ms)} & \multicolumn{3}{c|}{Memory (MB)} & \multicolumn{3}{c|}{Render (ms)} & \multicolumn{3}{c|}{DB-load (ms)} \\
|
|
|
|
\hline
|
|
|
|
\# & min & avg & max & \#"=avg & avg & start & stop & diff & min & avg & max & min & avg & max \\
|
|
|
|
\hline
|
|
|
|
1 & 339 & 659 & 2435 & 880.8 & 33.2 & 909.6 & 960.2 & 50.6 & 330 & 644 & 2375 & 218 & 343 & 815 \\ % 8808 - 332 ms (168+ 63+ 44+ 32+21+ 4) (#1,3-6,10)
|
|
|
|
2 & 267 & 332 & 388 & 832.1 & 28.1 & 959.7 & 1000.0 & 40.3 & 259 & 323 & 377 & 178 & 229 & 280 \\ % 17129 - 613 ms (313+111+ 82+ 48+42+17) (#1-3,5-6,8)
|
|
|
|
3 & 265 & 397 & 350 & 830.3 & 27.3 & 1001.0 & 1107.0 & 106.0 & 256 & 288 & 241 & 172 & 204 & 252 \\ % 25432 - 886 ms (455+156+125+ 64+63+23) (#1-3,5-6,8)
|
|
|
|
4 & 249 & 311 & 401 & 727.8 & 27.1 & 1108.0 & 1234.0 & 126.0 & 240 & 303 & 392 & 165 & 225 & 317 \\ % 32710 - 1157 ms (594+205+163+ 85+80+30) (#1-5,8)
|
|
|
|
5 & 268 & 296 & 325 & 931.9 & 28.0 & 1236.0 & 1239.0 & 3.0 & 260 & 288 & 318 & 192 & 217 & 244 \\ % 42029 - 1437 ms (738+254+204+106+97+38) (#1-5,8)
|
|
|
|
\hline
|
|
|
|
\end{tabular}
|
|
|
|
}
|
|
|
|
\caption{Messung mit OpenJPA-Cache und Größe auf 1000 und 0 SoftReference}
|
|
|
|
\label{tbl:measure-ojpa-active-bigger-no-softref}
|
|
|
|
\end{table}
|
|
|
|
|
2024-09-10 00:46:35 +02:00
|
|
|
Der Vergleich zeigt, dass der Cache eine gute Optimierung bringt, aber dies nur dann gut funktioniert, wenn immer
|
|
|
|
wieder die gleichen Objekte ermittelt werden. Sobald die Anfragen im Wechsel gerufen werden oder einfach nur die Menge
|
|
|
|
der Objekte den Cache übersteigt, fällt die Verbesserung geringer aus.
|
2024-08-19 21:00:19 +02:00
|
|
|
|
2024-09-01 23:06:28 +02:00
|
|
|
\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 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 \ref{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.
|
|
|
|
|
2024-09-08 00:33:09 +02:00
|
|
|
% document, documentaddresseeperson, first/last, documentcoauthorperson, count und documentfacsimile
|
2024-09-01 23:06:28 +02:00
|
|
|
\begin{table}[h!]
|
|
|
|
\centering
|
2024-09-08 00:33:09 +02:00
|
|
|
\resizebox{\textwidth}{!}{
|
|
|
|
\begin{tabular}{|r|r|r|r|r|r|r|r|r|r|r|r|r|r|r|}
|
2024-09-01 23:06:28 +02:00
|
|
|
\hline
|
2024-09-08 00:33:09 +02:00
|
|
|
& \multicolumn{3}{c|}{Aufrufzeit (ms)} & \multicolumn{2}{c|}{Queries (ms)} & \multicolumn{3}{c|}{Memory (MB)} & \multicolumn{3}{c|}{Render (ms)} & \multicolumn{3}{c|}{DB-load (ms)} \\
|
2024-09-01 23:06:28 +02:00
|
|
|
\hline
|
2024-09-08 00:33:09 +02:00
|
|
|
\# & min & avg & max & \#"=avg & avg & start & stop & diff & min & avg & max & min & avg & max \\
|
2024-09-01 23:06:28 +02:00
|
|
|
\hline
|
2024-09-08 00:33:09 +02:00
|
|
|
1 & 409 & 771 & 2660 & 1222.4 & xxx & 850.4 & 982.8 & 132.4 & 366 & 633 & 2019 & 254 & 364 & 758 \\ % 12224 - 332 ms (168+ 63+ 44+ 32+21+ 4) (#1,3-6,10)
|
|
|
|
2 & 336 & 387 & 504 & 1208.0 & xxx & 982.9 & 1113.0 & 130.1 & 310 & 374 & 433 & 221 & 268 & 345 \\ % 24304 -
|
|
|
|
3 & 312 & 373 & 422 & 1208.0 & xxx & 1114.0 & 1221.0 & 107.0 & 295 & 401 & 658 & 216 & 320 & 570 \\ % 36384 -
|
|
|
|
4 & 288 & 363 & 471 & 1208.0 & xxx & 1239.0 & 1474.0 & 235.0 & 269 & 356 & 486 & 200 & 279 & 405 \\ % 48464 -
|
|
|
|
5 & 325 & 398 & 535 & 1208.0 & xxx & 1474.0 & 1666.0 & 192.0 & 280 & 466 & 804 & 208 & 390 & 725 \\ % 60544 -
|
2024-09-01 23:06:28 +02:00
|
|
|
\hline
|
|
|
|
\end{tabular}
|
2024-09-08 00:33:09 +02:00
|
|
|
}
|
2024-09-01 23:06:28 +02:00
|
|
|
\caption{Messung mit aktiviertem Cached Queries}
|
|
|
|
\label{tbl:measure-cached-queries}
|
|
|
|
\end{table}
|
|
|
|
|
2024-09-12 23:02:22 +02:00
|
|
|
\section{Caching im JPA}
|
2024-08-19 21:00:19 +02:00
|
|
|
\label{sec:performance-investigation-application:caching-jpa}
|
|
|
|
|
2024-09-11 19:34:36 +02:00
|
|
|
%hier ein test vom acf: \acf{JPA}. aber ich seh ihn nicht
|
|
|
|
|
2024-09-01 23:06:28 +02:00
|
|
|
\mytodos{muss noch umgebaut werden, falsche Konfiguration erwischt}
|
|
|
|
|
2024-09-05 00:02:53 +02:00
|
|
|
%Die Cache-Einstellungen von \ac{JPA} werden über mehrere Einstellungen konfiguriert. Anhand von
|
|
|
|
%\texttt{eclipselink.query-results-cache} wird definiert, dass die Ergebnisse von benannten Abfragen im Cache
|
|
|
|
%gespeichert werden. Für den Zugriff in den Cache, wird neben den Namen noch die übergebenen Parameter
|
|
|
|
%berücksichtigt.
|
|
|
|
%% https://eclipse.dev/eclipselink/documentation/2.5/concepts/cache008.htm
|
2024-09-01 23:06:28 +02:00
|
|
|
|
2024-09-05 00:02:53 +02:00
|
|
|
%Der geteilte Cache, der für die Dauer der persistenten Einheit (EntityManagerFactory oder der Server) vorhanden ist,
|
|
|
|
%kann über \texttt{eclipselink.cache.shared.default} gesteuert werden. Dieser kann nur aktiviert oder deaktiviert werden.
|
|
|
|
%% https://wiki.eclipse.org/EclipseLink/Examples/JPA/Caching
|
2024-08-19 21:00:19 +02:00
|
|
|
|
2024-09-05 00:02:53 +02:00
|
|
|
%Mit \texttt{eclipselink.cache.size.default} wird die initiale Größe des Caches definiert, hierbei ist der Standardwert
|
|
|
|
%100. Die Objekt werden nicht direkt aus dem Cache entfernt, sondern erst nachdem der \ac{GC} diese freigeben hat.
|
|
|
|
%Zusätzlich wird über \texttt{eclipselink.cache.type.default} die Art des Caching gesteuert. Die Einstellung mit dem
|
|
|
|
%höchsten Speicherbedarf ist \textit{FULL}, bei dem alle Objekte im Cache bleiben, außer sie werden explizit gelöscht.
|
|
|
|
%Die Einstellung \textit{SOFT} und \textit{WEAK} sind sehr ähnlich, der unterschied ist die Referenzierung auf die
|
|
|
|
%Entität. Bei \textit{WEAK} bleiben die Objekte nur solange erhalten, wie die Anwendung selbst eine Referenz auf die
|
|
|
|
%Objekte fest hält. Im Gegensatz dazu bleibt bei \textit{SOFT} die Referenz so lange bestehen, bis der \ac{GC} wegen
|
|
|
|
%zu wenig Speicher Objekte aus dem Cache entfernt.
|
|
|
|
%% https://eclipse.dev/eclipselink/documentation/2.5/concepts/cache002.htm
|
2024-08-19 21:00:19 +02:00
|
|
|
|
2024-09-05 00:02:53 +02:00
|
|
|
%Um den Cache zu deaktivieren wurden beiden Einstellungen auf \textit{false} gestellt, die Größe auf 0 und der Cache-Typ
|
2024-09-10 00:46:35 +02:00
|
|
|
%auf \textit{NONE}. Hierbei lag die maximale gemessene Laufzeit des ersten Aufrufs bei circa 1300 ms und es wurden 12219
|
2024-09-05 00:02:53 +02:00
|
|
|
%Abfragen an die Datenbank gestellt. Bei den nachfolgenden Aufrufe lag die Aufrufzeit im Durchschnitt bei 350 ms und
|
|
|
|
%12080 Abfragen.
|
2024-08-19 21:00:19 +02:00
|
|
|
|
2024-09-05 00:02:53 +02:00
|
|
|
%Um den Cache wieder zu aktivieren wurden die Einstellungen auf \textit{true} gestellt, die Größe auf den Standardwert
|
|
|
|
%von 100 und der Cache-Type auf \textit{SOFT} gestellt. Hierbei wurde eine maximale Laufzeit beim ersten Aufruf ebenfalls
|
|
|
|
%von 1300 ms gemessen und es wurden 12218 Abfragen abgesetzt. Bei den nachfolgenden Aufrufen lag die Aufrufzeit im
|
|
|
|
%Durchschnitt bei 340 ms.
|
2024-08-19 21:00:19 +02:00
|
|
|
|
2024-09-05 00:02:53 +02:00
|
|
|
%Bei WEAK hat sich die Speichernutzung nur um 5MB gesteigert
|
2024-08-19 21:00:19 +02:00
|
|
|
|
2024-09-05 00:02:53 +02:00
|
|
|
%\mytodos{in einer Tabelle oder Graphen darstellen?}
|
2024-08-19 21:00:19 +02:00
|
|
|
|
2024-09-05 00:02:53 +02:00
|
|
|
%Wie man an den Daten erkennen kann, wird der Cache vom \ac{JPA} für diese Abfrage nicht verwendet, sonst müssten die
|
|
|
|
%Anzahl der Abfragen an die Datenbank drastisch reduziert werden. Selbst die Laufzeit ändert sich nur marginal.
|
2024-08-19 21:00:19 +02:00
|
|
|
|
2024-09-12 23:02:22 +02:00
|
|
|
\section{Caching in EJB}
|
2024-08-19 21:00:19 +02:00
|
|
|
\label{sec:performance-investigation-application:caching-ejb}
|
|
|
|
|
|
|
|
Die Cache-Einstellungen des \ac{EJB} sind in der Admin-Oberfläche des Payara-Servers zu erreichen. Hier
|
|
|
|
\mytodos{Cache config noch definieren}
|
|
|
|
|
|
|
|
\begin{table}[h!]
|
|
|
|
\centering
|
2024-09-08 00:33:09 +02:00
|
|
|
\resizebox{\textwidth}{!}{
|
|
|
|
\begin{tabular}{|r|r|r|r|r|r|r|r|r|r|r|r|r|r|r|}
|
2024-08-19 21:00:19 +02:00
|
|
|
\hline
|
2024-09-08 00:33:09 +02:00
|
|
|
& \multicolumn{3}{c|}{Aufrufzeit (ms)} & \multicolumn{2}{c|}{Queries (ms)} & \multicolumn{3}{c|}{Memory (MB)} & \multicolumn{3}{c|}{Render (ms)} & \multicolumn{3}{c|}{DB-load (ms)} \\
|
2024-08-19 21:00:19 +02:00
|
|
|
\hline
|
2024-09-08 00:33:09 +02:00
|
|
|
\# & min & avg & max & \#"=avg & avg & start & stop & diff & min & avg & max & min & avg & max \\
|
2024-08-19 21:00:19 +02:00
|
|
|
\hline
|
|
|
|
1 & 416 & 554 & 1269 & 12237 & 840.31 & 998.07 & 157.76 \\
|
|
|
|
2 & 299 & 394 & 749 & 12080 & 973.20 & 1101.37 & 128.17 \\
|
|
|
|
3 & 293 & 324 & 382 & 12080 & 1092.00 & 1192.87 & 100.87 \\
|
|
|
|
4 & 281 & 318 & 398 & 12080 & 1191.25 & 1305.29 & 114.04 \\
|
|
|
|
\hline
|
|
|
|
\end{tabular}
|
2024-09-08 00:33:09 +02:00
|
|
|
}
|
2024-08-19 21:00:19 +02:00
|
|
|
\caption{Messung mit \ac{EJB}-Cache}
|
|
|
|
\label{tbl:measure-ejb-cache-active}
|
|
|
|
\end{table}
|
|
|
|
|
2024-09-12 23:02:22 +02:00
|
|
|
\section{Abfragen JPQL}
|
2024-08-19 21:00:19 +02:00
|
|
|
\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 \ref{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]
|
|
|
|
SELECT DISTINCT d FROM Document d
|
|
|
|
LEFT JOIN FETCH d.authorPerson
|
|
|
|
LEFT JOIN FETCH d.coauthorPersonSet
|
|
|
|
LEFT JOIN FETCH d.addresseePersonSet
|
|
|
|
WHERE d.validUntil > :now
|
|
|
|
AND d.isPublishedInDb = :published
|
|
|
|
ORDER BY d.documentId ASC
|
|
|
|
\end{lstlisting}
|
|
|
|
|
|
|
|
In dem dazugehörigen Code am Server wird der JPQL-Code als NamedQuery hinterlegt und über den Name \textit{Document.findAll}
|
2024-09-01 23:06:28 +02:00
|
|
|
referenziert. Eine Veränderung der Abfrage ist hier leider nicht möglich, wie man im Code \ref{lst:jpql-document-list}
|
2024-08-19 21:00:19 +02:00
|
|
|
sehen kann.
|
|
|
|
|
|
|
|
\begin{lstlisting}[language=Java,caption={Java JPQL Dokumentenliste},label=lst:jpql-document-list]
|
|
|
|
List<Document> myResultList = createNamedTypedQuery("Document.findAll")
|
|
|
|
.setParameter("now", _IncludeDeleted ? new Date(0) : Date.from(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant()))
|
|
|
|
.setParameter("published", true)
|
|
|
|
.setFirstResult(_Start)
|
|
|
|
.setMaxResults(_Size)
|
|
|
|
.setHint("javax.persistence.query.fetchSize", _Size)
|
|
|
|
.getResultList();
|
|
|
|
|
|
|
|
// Uebergabe der Ergebnisliste
|
|
|
|
if(myResultList != null && !myResultList.isEmpty()) {
|
|
|
|
myResult.addAll(myResultList);
|
|
|
|
}
|
|
|
|
\end{lstlisting}
|
|
|
|
|
2024-09-01 23:06:28 +02:00
|
|
|
Da dieser Code direkt so aus dem Projekt kommt, wird hierfür keine gesonderte Zeitmessung durchgeführt, da diese der
|
|
|
|
Messung \ref{tbl:measure-without-cache} entspricht.
|
2024-08-19 21:00:19 +02:00
|
|
|
|
2024-09-10 00:46:35 +02:00
|
|
|
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 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
|
|
|
|
mit Vorsicht zu verwenden.
|
|
|
|
|
|
|
|
Mit dem Hint \textit{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
|
|
|
|
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 CoAuthoren 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 Konfiguraiton für Unterklassen definiert. Die
|
|
|
|
Möglichkeiten entsprechen der vom \textit{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 Objekte
|
|
|
|
mitgeladen werden. Damit wird zwar die Abfrage beschleunigt, aber nur aufgrund fehlender Datenbestände.
|
|
|
|
\mytodos{besser formulieren}
|
|
|
|
|
2024-08-19 21:00:19 +02:00
|
|
|
\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
|
|
|
|
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
|
|
|
|
versorgt werden, wie es in \ref{lst:criteria-api} gezeigt wird.
|
|
|
|
|
|
|
|
\begin{lstlisting}[language=Java,caption={Criteria API Dokumentenliste},label=lst:criteria-api]
|
|
|
|
CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
|
|
|
|
CriteriaQuery<Document> cq = cb.createQuery(Document.class);
|
|
|
|
Root<Document> from = cq.from(Document.class);
|
|
|
|
ParameterExpression<Boolean> includedPara = cb.parameter(Boolean.class, "published");
|
|
|
|
ParameterExpression<Date> validPart = cb.parameter(Date.class, "now");
|
|
|
|
|
|
|
|
CriteriaQuery<Document> select = cq.select(from)
|
|
|
|
.where(cb.and(
|
|
|
|
cb.equal(from.get("isPublishedInDb"), includedPara),
|
|
|
|
cb.greaterThan(from.get("validUntil"), validPart)
|
|
|
|
));
|
|
|
|
TypedQuery<Document> typedQuery = getEntityManager().createQuery(select)
|
|
|
|
.setParameter("now", _IncludeDeleted ? new Date(0) : Date.from(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant()))
|
|
|
|
.setParameter("published", true)
|
|
|
|
.setFirstResult(_Start)
|
|
|
|
.setMaxResults(_Size)
|
|
|
|
.setHint("javax.persistence.query.fetchSize", _Size);
|
|
|
|
List<Document> myResultList = typedQuery.getResultList();
|
|
|
|
|
|
|
|
// Uebergabe der Ergebnisliste
|
|
|
|
if (myResultList != null && !myResultList.isEmpty()) {
|
|
|
|
myResult.addAll(myResultList);
|
|
|
|
}
|
|
|
|
\end{lstlisting}
|
|
|
|
|
|
|
|
Wie in der Messung \ref{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 Abfragen
|
|
|
|
in den Java-Objekten fast identisch sind. Und in der Datenbank sind die Anfragen identisch zu denen über JPQL.
|
|
|
|
|
|
|
|
\begin{table}[h!]
|
|
|
|
\centering
|
2024-09-08 00:33:09 +02:00
|
|
|
\resizebox{\textwidth}{!}{
|
|
|
|
\begin{tabular}{|r|r|r|r|r|r|r|r|r|r|r|r|r|r|r|}
|
2024-08-19 21:00:19 +02:00
|
|
|
\hline
|
2024-09-08 00:33:09 +02:00
|
|
|
& \multicolumn{3}{c|}{Aufrufzeit (ms)} & \multicolumn{2}{c|}{Queries (ms)} & \multicolumn{3}{c|}{Memory (MB)} & \multicolumn{3}{c|}{Render (ms)} & \multicolumn{3}{c|}{DB-load (ms)} \\
|
2024-08-19 21:00:19 +02:00
|
|
|
\hline
|
2024-09-08 00:33:09 +02:00
|
|
|
\# & min & avg & max & \#"=avg & avg & start & stop & diff & min & avg & max & min & avg & max \\
|
2024-08-19 21:00:19 +02:00
|
|
|
\hline
|
|
|
|
1 & 396 & 572 & 1535 & 12173 & 796.59 & 970.10 & 173.51 \\
|
|
|
|
2 & 333 & 366 & 397 & 12080 & 982.28 & 1064.12 & 81.84 \\
|
|
|
|
3 & 286 & 339 & 554 & 12080 & 1048.12 & 1162.92 & 114.80 \\
|
|
|
|
4 & 293 & 317 & 388 & 12080 & 1150.43 & 1263.77 & 113.34 \\
|
|
|
|
\hline
|
|
|
|
\end{tabular}
|
2024-09-08 00:33:09 +02:00
|
|
|
}
|
2024-08-19 21:00:19 +02:00
|
|
|
\caption{Messung mit Criteria-API ohne Cache}
|
|
|
|
\label{tbl:measure-criteria-api}
|
|
|
|
\end{table}
|
|
|
|
|
2024-09-10 00:46:35 +02:00
|
|
|
Daher bringt die Criteria API keinen performance Vorteil gegenüber der JPQL"=Implementierung. Somit können beide
|
2024-09-01 23:06:28 +02:00
|
|
|
Implementierung ohne bedenken gegeneinander ausgetauscht werden, und die verwendet werden, die für den Anwendungsfall
|
|
|
|
einfacher umzusetzen ist.
|
2024-08-19 21:00:19 +02:00
|
|
|
|
2024-09-10 00:46:35 +02:00
|
|
|
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
|
|
|
|
\textit{parallel} gestellt wird. Hier wird ebenfalls die Anzahl der Anfragen reduziert und damit auch die
|
|
|
|
Geschwindigkeit optimiert.
|
|
|
|
|
2024-08-19 21:00:19 +02:00
|
|
|
\section{materialized views}
|
|
|
|
\label{sec:performance-investigation-application:materialized-views}
|
|
|
|
|
|
|
|
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
|
2024-09-01 23:06:28 +02:00
|
|
|
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 igoriert ewrden.
|
2024-08-19 21:00:19 +02:00
|
|
|
|
|
|
|
In diesem Test, wurde zusätzlich zur normalen Abfragen noch die nachfolgenden einzelabfragen als Sub-Selects
|
|
|
|
hinzugefügt, wie in \ref{lst:sql-materialized-view} zu sehen. Somit können die nachfolgenden einzelnen Abfragen
|
|
|
|
eingespart werden. Dies wiederrum geht aber auf die Performance der Erstellung der Sicht und ihrer Aktualisierung.
|
|
|
|
|
|
|
|
\begin{lstlisting}[language=SQL,caption={SQL Materialized View},label=lst:sql-materialized-view]
|
|
|
|
CREATE MATERIALIZED VIEW searchdocument AS
|
|
|
|
SELECT
|
|
|
|
d.id, d.documentId, d.datetype, d.startdatestatus, d.startyear,
|
|
|
|
d.startmonth, d.startday, d.enddatestatus, d.endyear, d.endmonth,
|
|
|
|
d.endday,
|
|
|
|
(
|
|
|
|
SELECT
|
|
|
|
jsonb_build_object(
|
|
|
|
'personId', hp.personid,
|
|
|
|
'surname', hp.surname,
|
|
|
|
'firstname', hp.firstname,
|
|
|
|
'dateBirth', json_build_object(
|
|
|
|
'year', hp.birthstartyear,
|
|
|
|
'month', hp.birthstartmonth,
|
|
|
|
'day', hp.birthstartday
|
|
|
|
),
|
|
|
|
'dateDeath', json_build_object(
|
|
|
|
'year', hp.deathstartyear,
|
|
|
|
'month', hp.deathstartmonth,
|
|
|
|
'day', hp.deathstartday
|
|
|
|
)
|
|
|
|
)
|
|
|
|
FROM historicalperson hp
|
|
|
|
WHERE hp.id = d.authorperson_id
|
|
|
|
AND hp.validuntil > NOW()
|
|
|
|
) as author,
|
|
|
|
(
|
|
|
|
SELECT
|
|
|
|
jsonb_agg(jsonb_build_object(
|
|
|
|
'personId', hcap.personid,
|
|
|
|
'surname', hcap.surname,
|
|
|
|
'firstname', hcap.firstname,
|
|
|
|
'dateBirth', json_build_object(
|
|
|
|
'year', hcap.birthstartyear,
|
|
|
|
'month', hcap.birthstartmonth,
|
|
|
|
'day', hcap.birthstartday
|
|
|
|
),
|
|
|
|
'dateDeath', json_build_object(
|
|
|
|
'year', hcap.deathstartyear,
|
|
|
|
'month', hcap.deathstartmonth,
|
|
|
|
'day', hcap.deathstartday
|
|
|
|
)
|
|
|
|
))
|
|
|
|
FROM documentcoauthorperson dcap
|
|
|
|
JOIN historicalperson hcap
|
|
|
|
ON hcap.id = dcap.authorperson_id
|
|
|
|
AND dcap.validuntil > NOW()
|
|
|
|
AND hcap.validuntil > NOW()
|
|
|
|
WHERE dcap.document_id = d.id
|
|
|
|
) AS coauthors,
|
|
|
|
(
|
|
|
|
SELECT
|
|
|
|
jsonb_agg(jsonb_build_object(
|
|
|
|
'personId', hap.personid,
|
|
|
|
'surname', hap.surname,
|
|
|
|
'firstname', hap.firstname,
|
|
|
|
'dateBirth', json_build_object(
|
|
|
|
'year', hap.birthstartyear,
|
|
|
|
'month', hap.birthstartmonth,
|
|
|
|
'day', hap.birthstartday
|
|
|
|
),
|
|
|
|
'dateDeath', json_build_object(
|
|
|
|
'year', hap.deathstartyear,
|
|
|
|
'month', hap.deathstartmonth,
|
|
|
|
'day', hap.deathstartday
|
|
|
|
)
|
|
|
|
))
|
|
|
|
FROM documentaddresseeperson dap
|
|
|
|
JOIN historicalperson hap
|
|
|
|
ON hap.id = dap.addresseeperson_id
|
|
|
|
AND dap.validuntil > NOW()
|
|
|
|
AND hap.validuntil > NOW()
|
|
|
|
WHERE dap.document_id = d.id
|
|
|
|
) AS addressees,
|
|
|
|
sc.city, d.documentcategory, d.ispublishedindb, d.createdat,
|
|
|
|
d.modifiedat, d.validuntil
|
|
|
|
FROM document d
|
|
|
|
LEFT JOIN sitecity sc ON sc.id = d.city_id;
|
|
|
|
\end{lstlisting}
|
|
|
|
|
2024-09-10 23:33:57 +02:00
|
|
|
Zusätzlich werden noch einige Indexe hinzugefügt, für eine bessere Performance bei der Abfrage, wie in
|
|
|
|
\ref{lst:sql-materialized-view-index} zu sehen.
|
|
|
|
|
|
|
|
\begin{lstlisting}[language=SQL,caption={SQL Materialized View},label=lst:sql-materialized-view-index]
|
|
|
|
CREATE INDEX idx_searchdocument_documentid
|
|
|
|
ON searchdocument (documentid);
|
|
|
|
|
|
|
|
CREATE INDEX idx_searchdocument_author_surname_firstname
|
|
|
|
ON searchdocument ((author->>'surname'), (author->>'firstname'));
|
|
|
|
|
|
|
|
CREATE INDEX idx_searchdocument_startdate
|
|
|
|
ON searchdocument (startyear, startmonth, startday);
|
|
|
|
|
|
|
|
CREATE INDEX idx_searchdocument_addressees_first_entry
|
|
|
|
ON searchdocument
|
2024-09-12 23:02:22 +02:00
|
|
|
( ((addressees->0->>'surname')::text)
|
|
|
|
, ((addressees->0->>'firstname')::text));
|
2024-09-10 23:33:57 +02:00
|
|
|
|
|
|
|
CREATE INDEX idx_searchdocument_city
|
|
|
|
ON searchdocument (city);
|
|
|
|
|
|
|
|
CREATE INDEX idx_searchdocument_documentcategory
|
|
|
|
ON searchdocument (documentcategory);
|
|
|
|
\end{lstlisting}
|
|
|
|
|
2024-08-21 00:30:20 +02:00
|
|
|
% document, first/last, documentaddresseeperson, documentcoauthorperson, documentfacsimile und count
|
2024-08-21 23:44:10 +02:00
|
|
|
% document, count, first/last
|
2024-08-19 21:00:19 +02:00
|
|
|
\begin{table}[h!]
|
|
|
|
\centering
|
2024-09-08 00:33:09 +02:00
|
|
|
\resizebox{\textwidth}{!}{
|
|
|
|
\begin{tabular}{|r|r|r|r|r|r|r|r|r|r|r|r|r|r|r|}
|
2024-08-19 21:00:19 +02:00
|
|
|
\hline
|
2024-09-08 00:33:09 +02:00
|
|
|
& \multicolumn{3}{c|}{Aufrufzeit (ms)} & \multicolumn{2}{c|}{Queries (ms)} & \multicolumn{3}{c|}{Memory (MB)} & \multicolumn{3}{c|}{Render (ms)} & \multicolumn{3}{c|}{DB-load (ms)} \\
|
2024-08-19 21:00:19 +02:00
|
|
|
\hline
|
2024-09-08 00:33:09 +02:00
|
|
|
\# & min & avg & max & \#"=avg & avg & start & stop & diff & min & avg & max & min & avg & max \\
|
2024-08-19 21:00:19 +02:00
|
|
|
\hline
|
2024-08-21 23:44:10 +02:00
|
|
|
1 & 203 & 315 & 808 & 17.8 & 3.0 & 851.4 & 883.9 & 32.5 \\ % 178 - 30 ms (19+11+0) (#2,4,8)
|
|
|
|
2 & 154 & 172 & 187 & 9.0 & 2.2 & 883.2 & 887.0 & 3.8 \\ % 268 - 52 ms (33+18+1) (#2,3,8)
|
|
|
|
3 & 145 & 151 & 163 & 9.0 & 2.8 & 887.7 & 895.3 & 7.6 \\ % 358 - 80 ms (52+27+1) (#2,3,8)
|
|
|
|
4 & 132 & 143 & 152 & 9.0 & 2.8 & 896.0 & 900.0 & 4.0 \\ % 448 - 108 ms (70+37+1) (#2,3,8)
|
|
|
|
5 & 121 & 125 & 132 & 9.0 & 2.4 & 900.6 & 901.0 & 0.4 \\ % 538 - 132 ms (85+46+1) (#2,3,8)
|
2024-08-19 21:00:19 +02:00
|
|
|
\hline
|
|
|
|
\end{tabular}
|
2024-09-08 00:33:09 +02:00
|
|
|
}
|
2024-08-19 21:00:19 +02:00
|
|
|
\caption{Messung mit Materialized View}
|
|
|
|
\label{tbl:measure-materialized-view}
|
|
|
|
\end{table}
|
|
|
|
|
|
|
|
Wie in Tabelle \ref{tbl:measure-materialized-view} zu sehen, bringt die Verwendung der Materialized View ein Verbesserung
|
|
|
|
in verschiedenen Punkten. Zum einen ist eine Verbesserung der Aufrufzeiten zu erkennen, zusätzlich fällt der
|
2024-09-01 23:06:28 +02:00
|
|
|
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 einzelabfragen für die Adressen der Personen
|
|
|
|
und der CoAutoren komplett entfallen.
|
2024-08-19 21:00:19 +02:00
|
|
|
|
|
|
|
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 Sicht
|
|
|
|
mit integriert. Dies bedeutet eine Erweiterung der Sicht aus \ref{lst:sql-materialized-view} um
|
|
|
|
\ref{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()
|
|
|
|
AND d.ispublishedindb = true;
|
|
|
|
\end{lstlisting}
|
|
|
|
|
|
|
|
Nach dem Anpassungen haben sich dann die Werte aus \ref{tbl:measure-materialized-view-ext} ergeben.
|
|
|
|
|
|
|
|
\begin{table}[h!]
|
|
|
|
\centering
|
2024-09-08 00:33:09 +02:00
|
|
|
\resizebox{\textwidth}{!}{
|
|
|
|
\begin{tabular}{|r|r|r|r|r|r|r|r|r|r|r|r|r|r|r|}
|
2024-08-19 21:00:19 +02:00
|
|
|
\hline
|
2024-09-08 00:33:09 +02:00
|
|
|
& \multicolumn{3}{c|}{Aufrufzeit (ms)} & \multicolumn{2}{c|}{Queries (ms)} & \multicolumn{3}{c|}{Memory (MB)} & \multicolumn{3}{c|}{Render (ms)} & \multicolumn{3}{c|}{DB-load (ms)} \\
|
2024-08-19 21:00:19 +02:00
|
|
|
\hline
|
2024-09-08 00:33:09 +02:00
|
|
|
\# & min & avg & max & \#"=avg & avg & start & stop & diff & min & avg & max & min & avg & max \\
|
2024-08-19 21:00:19 +02:00
|
|
|
\hline
|
2024-09-08 00:33:09 +02:00
|
|
|
1 & 241 & 348 & 859 & 16.8 & xxx & 896.0 & 932.4 & xxxx & 232 & 331 & 803 & 132 & 174 & 334 \\ % 168 -
|
|
|
|
2 & 164 & 194 & 225 & 9.0 & xxx & 933.3 & 935.9 & xxxx & 154 & 185 & 215 & 79 & 99 & 117 \\ % 258 -
|
|
|
|
3 & 147 & 161 & 179 & 9.0 & xxx & 935.8 & 938.8 & 3.0 & 139 & 152 & 167 & 68 & 77 & 86 \\ % 348 -
|
|
|
|
4 & 135 & 145 & 183 & 9.0 & xxx & 939.4 & 936.0 & -3.4 & 127 & 137 & 174 & 70 & 73 & 75 \\ % 438 -
|
|
|
|
5 & 126 & 137 & 154 & 9.0 & xxx & 936.1 & 939.1 & 3.0 & 118 & 129 & 143 & 66 & 72 & 79 \\ % 528 -
|
2024-08-19 21:00:19 +02:00
|
|
|
\hline
|
|
|
|
\end{tabular}
|
2024-09-08 00:33:09 +02:00
|
|
|
}
|
2024-08-19 21:00:19 +02:00
|
|
|
\caption{Messung mit erweiterter Materialized View}
|
|
|
|
\label{tbl:measure-materialized-view-ext}
|
|
|
|
\end{table}
|
|
|
|
|
2024-09-08 20:39:29 +02:00
|
|
|
Da bei der 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
|
2024-09-10 00:46:35 +02:00
|
|
|
und das durcharbeiten der Ergebnisse bestimmt. Hierbei lagen die Zeiten bei circa 1 ms für das reine Datenladen und 3 ms
|
2024-09-08 20:39:29 +02:00
|
|
|
für den Aufruf der \textit{map}"=Funktion. Sobald mal 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 kostet die meiste Zeit.
|
|
|
|
|
2024-09-11 19:34:36 +02:00
|
|
|
Bei der Verwendung des Hints \textit{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.
|
|
|
|
|
|
|
|
Das aktivieren der Cache-Optionen wie in \ref{sec:performance-investigation-application:caching-openjpa} oder in
|
|
|
|
\ref{sec:performance-investigation-application:caching-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.
|
|
|
|
|
2024-09-10 23:33:57 +02:00
|
|
|
Nun wird noch geprüft, welche Performance das parsen der Json-Informationen im Server benötigt. Im ersten Schritt wird
|
|
|
|
die Parse-Funktion auskommentiert und die Seite nochmal 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
|
|
|
|
\ac{Json}"=Daten benötigt. Hierfür werden die Daten in einem versteckten \textbf{span}"=Element hinterlegt, wie es im
|
|
|
|
Beispiel \ref{lst:jsf-datatable-json} zu sehen ist. Die hinterlegte \ac{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 neu
|
|
|
|
übertragenen Elemente in eine lesbare Form gebracht werden.
|
|
|
|
|
|
|
|
\begin{lstlisting}[language=xml,caption={DataTable mit Json},label=lst:jsf-datatable-json]
|
|
|
|
<p:ajax event="page" oncomplete="convertJsonData()"/>
|
|
|
|
<p:column id="author" headerText="#{lang.List_Docs_Author}" sortable="true" sortBy="#{myObj.surname}">
|
|
|
|
<h:outputText styleClass="json-convert" style="display: none;" value="#{myObj.authorJson}"/>
|
|
|
|
</p:column>
|
|
|
|
<p:column id="Addressee"
|
|
|
|
headerText="#{lang.List_Docs_Addressee}"
|
|
|
|
sortable="true"
|
|
|
|
sortBy="#{(myObj.addresseePersonSet!=null and myObj.addresseePersonSet.size() > 0)?myObj.addresseePersonSet[0].addresseePerson:''}">
|
|
|
|
<h:outputText styleClass="json-convert" style="display: none;" value="#{myObj.adresseeJson}" data="abc"/>
|
|
|
|
</p:column>
|
|
|
|
\end{lstlisting}
|
|
|
|
|
|
|
|
Um nun die übertragenen \ac{Json}"=Daten in eine darstellbare Form zu bringen, benötigt man noch eine
|
|
|
|
JavaScript"=Funktion. Diese Funktion \ref{lst:jsf-datatable-json-convert} wird ermittelt erst alle versteckten Elemente,
|
|
|
|
parsed den Inhalt und erstellt neue \ac{HTML}"=Elemente mit dem darzustellenden Inhalt. Zusätzlich wird noch eine
|
|
|
|
Zeitmessung mit 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 gerufen.
|
|
|
|
|
|
|
|
\begin{lstlisting}[language=javascript,caption={Wandeln von Json nach Html},label=lst:jsf-datatable-json-convert]
|
|
|
|
function isEmpty(str) {
|
|
|
|
return (str === null) || (str === undefined) || (typeof str === "string" && str.length === 0);
|
|
|
|
}
|
|
|
|
function convertJsonData() {
|
|
|
|
let $jsonObj = $(".json-convert")
|
|
|
|
, start = new Date()
|
|
|
|
;
|
|
|
|
|
|
|
|
$.each($jsonObj, function() {
|
|
|
|
let json = this.innerHTML
|
|
|
|
, strEmpty = (json === null) || (typeof json === "string" && json.length === 0)
|
|
|
|
, jsonDat = strEmpty ? null : JSON.parse(json)
|
|
|
|
;
|
|
|
|
if(!strEmpty) {
|
|
|
|
let res = ""
|
|
|
|
, $that = $(this)
|
|
|
|
, $par = $that.parent()
|
|
|
|
;
|
|
|
|
$.each(jsonDat, function() {
|
|
|
|
let hasOnlyOne = isEmpty(this.surname) || isEmpty(this.firstname)
|
|
|
|
, pseudonymExists = !isEmpty(this.pseudonym)
|
|
|
|
, namePart = "<span>" + (hasOnlyOne ? this.surname + this.firstname : this.surname + ", " + this.firstname) + "</span><br/>"
|
|
|
|
, pseudoPart = pseudonymExists ? "<span class='w3-small w3-text-dark-gray'>" + this.pseudonym + "</span><br/>" : ""
|
|
|
|
;
|
|
|
|
res += namePart + pseudoPart;
|
|
|
|
});
|
|
|
|
$par.append(res);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
let end = new Date()
|
|
|
|
, diff = (end - start)
|
|
|
|
;
|
|
|
|
console.log(Math.round(diff) + " ms");
|
|
|
|
}
|
|
|
|
|
|
|
|
$(document).ready(function() {
|
|
|
|
convertJsonData();
|
|
|
|
});
|
|
|
|
\end{lstlisting}
|
|
|
|
|
|
|
|
Da nun am Client 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.
|
2024-09-10 00:46:35 +02:00
|
|
|
|
2024-09-01 23:06:28 +02:00
|
|
|
%\mytodos{hier noch darauf eingehen, dass die Hauptarbeit nicht beim editieren sondern bei der Anzeige ist}
|
2024-08-19 21:00:19 +02:00
|
|
|
\mytodos{Hier könnte man auch den Query-Cache nochmal verwenden, da die anfragen nun fix wären}
|
|
|
|
\mytodos{Grundlagen zur Materialized-View noch hinterlegen}
|
|
|
|
|
|
|
|
\section{Statische Webseiten}
|
|
|
|
\label{sec:performance-investigation-application:static-website}
|
|
|
|
|
|
|
|
Wenn man die Dokumentenliste als statische Webseiten ablegt, werden die Zugriffszeiten sehr kurz sein. Darüber hinaus
|
2024-09-01 23:06:28 +02:00
|
|
|
funktionieren in statische Webseiten aber keine Suche oder eine Sortierung. Die Sortierung könnte durch das erstellen
|
|
|
|
von statischen Seite 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.
|
|
|
|
|
|
|
|
Die Umstellung der Suche auf Client!=Basis wäre noch eine Möglichkeit, dafür benötigen die Clients entsprechend
|
|
|
|
Leistung und es muss eine Referenzdatei erstellt werden, die alle Informationen über die Dokumente beinhaltet, nach der
|
|
|
|
gesucht werden kann.
|
2024-08-19 21:00:19 +02:00
|
|
|
|
2024-09-01 23:06:28 +02:00
|
|
|
Daher ist eine Umstellung auf statische Webseiten nicht sinnvoll.
|
2024-08-19 21:00:19 +02:00
|
|
|
|
|
|
|
\section{Client basierte Webseiten}
|
|
|
|
\label{sec:performance-investigation-application:client-side-rendering}
|
|
|
|
|
2024-09-05 00:02:53 +02:00
|
|
|
Als weitere Möglichkeit könnte man die Webseite so umbauen, dass die Daten erst im Nachgang über eine AJAX-Anfrage
|
|
|
|
ermittelt und die Sortierung und Aufteilung im Client durchgeführt wird. Hierbei wird aber je nach Datenmenge ein
|
|
|
|
großer Speicher am Client benötigt und die Rechenleistung wird auf den Client verschoben.
|
|
|
|
|
|
|
|
Dies wiederrum ist ein Vorteil für den Serverbetreiber, da durch die Verschiebung weniger Rechenleistung am Server
|
|
|
|
benötigt wird. Gleichzeitig würde man damit wiederrum schwächere Clients, wie Smartphones, aussperren, da bei diesem
|
|
|
|
die notwendige Rechenleistung fehlt, um die Webseite in annehmbarer Zeit darzustellen.
|