Daily CheckIn
This commit is contained in:
parent
a1af60e945
commit
8baff92dcc
9 changed files with 537 additions and 511 deletions
|
@ -88,8 +88,8 @@ hostname="http://localhost:8080/WedekindJSF-1.0.0"
|
||||||
# the Array of the Urls
|
# the Array of the Urls
|
||||||
url_arr=(
|
url_arr=(
|
||||||
"$hostname/index.xhtml"
|
"$hostname/index.xhtml"
|
||||||
"$hostname/view/document/list.xhtml"
|
#"$hostname/view/document/list.xhtml"
|
||||||
#"$hostname/view/document/listsearch.xhtml"
|
"$hostname/view/document/listsearch.xhtml"
|
||||||
#"$hostname/view/correspondent/list.xhtml"
|
#"$hostname/view/correspondent/list.xhtml"
|
||||||
#"$hostname/view/person/list.xhtml"
|
#"$hostname/view/person/list.xhtml"
|
||||||
)
|
)
|
||||||
|
|
|
@ -10,14 +10,18 @@
|
||||||
|
|
||||||
Um die Messungen etwas zu vereinfachen wurde ein Skript erstellt um die Aufrufe gesammelt durchzuführen. Um die
|
Um die Messungen etwas zu vereinfachen wurde ein Skript erstellt um die Aufrufe gesammelt durchzuführen. Um die
|
||||||
Messungen durchzuführen werden die Befehl, wie in \ref{lst:calling_script_exec} dargestellt aufgerufen.
|
Messungen durchzuführen werden die Befehl, wie in \ref{lst:calling_script_exec} dargestellt aufgerufen.
|
||||||
|
Durch die nummerierten Präfixe können im Nachgang über die pgBadger-Berichte die SQL-Abfragen verglichen werden.
|
||||||
|
Wichtig hierbei ist noch, dass vor dem ersten \textit{meascall}-Aufruf überprüft wird, ob die Docker-Container
|
||||||
|
gestartet und initialisiert sind. Wenn dies nicht der Fall ist, laufen die Abfragen ins leere. Am einfachsten ist das
|
||||||
|
über die Statistik von Docker zu ermitteln, ob die CPU-Auslastung auf einen niedrigen Level gefallen ist.
|
||||||
|
|
||||||
\includecode[bash]{chapters/thesis/appendix04_calling_script.sh}{lst:calling_script}{Calling Script}
|
\includecode[bash]{chapters/thesis/appendix04_calling_script.sh}{lst:calling_script}{Calling Script}
|
||||||
|
|
||||||
\begin{lstlisting}[language=Bash,caption={Aufrufe des Unterstützungsscriptes},label=lst:calling_script_exec]
|
\begin{lstlisting}[language=Bash,caption={Aufrufe des Unterstützungsscriptes},label=lst:calling_script_exec]
|
||||||
callscript.sh measinit
|
callscript.sh measinit
|
||||||
callscript.sh measres
|
callscript.sh -rppf=_testname measres
|
||||||
callscript.sh meascall
|
callscript.sh -rppf=_testname meascall
|
||||||
callscript.sh meascall
|
callscript.sh -rppf=_testname -rppn=2 meascall
|
||||||
callscript.sh meascall
|
callscript.sh -rppf=_testname -rppn=3 meascall
|
||||||
callscript.sh meascall
|
callscript.sh -rppf=_testname -rppn=4 meascall
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
|
@ -11,6 +11,7 @@ domain_log="$payara_path/logs/server.log"
|
||||||
script_path="/opt/docker/timing.sh"
|
script_path="/opt/docker/timing.sh"
|
||||||
pgbadger_out="/opt/docker/pgreport"
|
pgbadger_out="/opt/docker/pgreport"
|
||||||
report_postfix=""
|
report_postfix=""
|
||||||
|
report_postno=""
|
||||||
docker_name=dcpgbatch
|
docker_name=dcpgbatch
|
||||||
|
|
||||||
COMPOSE_FILE=/opt/docker/docker-compose.yaml
|
COMPOSE_FILE=/opt/docker/docker-compose.yaml
|
||||||
|
@ -54,13 +55,18 @@ pgconf() {
|
||||||
}
|
}
|
||||||
pgrp() {
|
pgrp() {
|
||||||
mkdir -p $pgbadger_out$report_postfix
|
mkdir -p $pgbadger_out$report_postfix
|
||||||
|
mkdir -p $pgbadger_out$report_postfix$report_postno
|
||||||
outPath=$pgbadger_out$report_postfix/bash.out
|
outPath=$pgbadger_out$report_postfix/bash.out
|
||||||
touch "$outPath"
|
touch "$outPath"
|
||||||
echo "" >>"$outPath"
|
echo "" >>"$outPath"
|
||||||
pgbadger -X -I -f jsonlog -j 10 -O $pgbadger_out$report_postfix $postgres_log_path/postgresql-*.json 2>&1 | tee -a "$outPath"
|
pgbadger -X -I -f jsonlog -j 10 -O $pgbadger_out$report_postfix$report_postno $postgres_log_path/postgresql-*.json 2>&1 | tee -a "$outPath"
|
||||||
}
|
}
|
||||||
pgrpres() {
|
pgrpres() {
|
||||||
rm -R $pgbadger_out$report_postfix
|
if ["$report_postfix" -eq ""]; then
|
||||||
|
rm -R $pgbadger_out
|
||||||
|
else
|
||||||
|
rm -R $pgbadger_out$report_postfix*
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
dccreate() {
|
dccreate() {
|
||||||
sudo docker compose -f $COMPOSE_FILE create --force-recreate
|
sudo docker compose -f $COMPOSE_FILE create --force-recreate
|
||||||
|
@ -78,13 +84,12 @@ dcstats() {
|
||||||
}
|
}
|
||||||
for name in "$@"; do
|
for name in "$@"; do
|
||||||
case $name in
|
case $name in
|
||||||
--rppf=*) report_postfix="${1#*=}" ;;
|
-rppf=*) report_postfix="${name#*=}" ;;
|
||||||
|
-rppn=*) report_postno="${name#*=}" ;;
|
||||||
gflog) gflog ;;
|
gflog) gflog ;;
|
||||||
gfconf) gfconf ;;
|
gfconf) gfconf ;;
|
||||||
gfscript) gfscript ;;
|
gfscript) gfscript ;;
|
||||||
gfrestart)
|
gfrestart) pgrpinit ;;
|
||||||
pgrpinit
|
|
||||||
;;
|
|
||||||
pginit) pginit ;;
|
pginit) pginit ;;
|
||||||
pglogls) pglogls ;;
|
pglogls) pglogls ;;
|
||||||
pglogrm) pglogrm ;;
|
pglogrm) pglogrm ;;
|
||||||
|
@ -98,6 +103,7 @@ for name in "$@"; do
|
||||||
dcstop) dcstop ;;
|
dcstop) dcstop ;;
|
||||||
dcstats) dcstats ;;
|
dcstats) dcstats ;;
|
||||||
measinit)
|
measinit)
|
||||||
|
pginit
|
||||||
pgrpres
|
pgrpres
|
||||||
pglogrm 0
|
pglogrm 0
|
||||||
dccreate
|
dccreate
|
||||||
|
@ -124,7 +130,8 @@ for name in "$@"; do
|
||||||
echo "ATTENTION: parameter must be defined in front of the commands!"
|
echo "ATTENTION: parameter must be defined in front of the commands!"
|
||||||
echo ""
|
echo ""
|
||||||
echo "*** parameter ***"
|
echo "*** parameter ***"
|
||||||
echo " --rppf=<val> Postfix name for the report-folder (used by gfscript, pgrp, pgrpres, measres, meascall)"
|
echo " -rppf=<val> Postfix name for the report-folder (used by gfscript, pgrp, pgrpres, measres, meascall)"
|
||||||
|
echo " -rppn=<val> Postfix number for the report-folder (used by pgrp, measres, meascall)"
|
||||||
echo ""
|
echo ""
|
||||||
echo "*** glassfish ***"
|
echo "*** glassfish ***"
|
||||||
echo " gflog Show and follow the log of the glassfish server with $domain_name"
|
echo " gflog Show and follow the log of the glassfish server with $domain_name"
|
||||||
|
|
|
@ -24,484 +24,3 @@ prüfen, die den Cache von OpenJPE auswerten}
|
||||||
|
|
||||||
\mytodos{Einbau der Messungen direkt in die Webseite bzw. in ein Log}
|
\mytodos{Einbau der Messungen direkt in die Webseite bzw. in ein Log}
|
||||||
\mytodos{Einstellung am postgresql um die queries mit zu loggen}
|
\mytodos{Einstellung am postgresql um die queries mit zu loggen}
|
||||||
|
|
||||||
\section{Untersuchung der Anwendung}
|
|
||||||
\label{sec:performance-checking:investigation-application}
|
|
||||||
|
|
||||||
Nun werden die unterschiedlichen Schichten betrachtet und möglichen Performance-Verbesserungen untersucht und deren
|
|
||||||
Vor"= und Nachteile herausgearbeitet.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
Zur ersten Untersuchung und der Bestimmung der Basis-Linie, wurde das Script ohne eine Änderung an dem Code und der
|
|
||||||
Konfiguration mehrfach aufgerufen. Hierbei hat sich gezeigt, dass der erste Aufruf nach dem Deployment ca. 1500 ms
|
|
||||||
gedauert hat. Die weiteren Aufrufe dauert dann im Durchschnitt bei 600 ms. Beim achten Aufruf des Scripts hat der
|
|
||||||
Server nicht mehr reagiert und im Log ist ein OutOfMemoryError protokolliert worden.
|
|
||||||
|
|
||||||
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
|
|
||||||
Auswertung zeigte, dass der Server mit ca. 1500 MB RSS Nutzung an seine Grenzen stößt. Diese Grenzen wurde durch die
|
|
||||||
Konfigurationsänderung im Payara-Server von \texttt{-Xmx512m} auf \texttt{-Xmx4096m} nach oben verschoben. Nun werden
|
|
||||||
ca. 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 ca. 4700 MB RSS. Bei allen Tests war noch mehr als die
|
|
||||||
Hälfte des verfügbaren Arbeitsspeichers unbenutzt.
|
|
||||||
|
|
||||||
Dies zeigt direkt, dass es ein problem in der Freigabe der Objekte gibt, da dass erhöhen des verwendbaren Arbeitsspeicher
|
|
||||||
das Problem nicht löst, sondern nur verschiebt.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
\mytodos{hier noch text einfügen, der erklärt wie die Spalten zu werten sind, also Aufrufzeit ist kürzer gleich besser}
|
|
||||||
|
|
||||||
\begin{table}[h!]
|
|
||||||
\centering
|
|
||||||
\begin{tabular}{|r|r|r|r|r|r|r|r|}
|
|
||||||
\hline
|
|
||||||
& \multicolumn{3}{|c|}{Aufrufzeit (ms)} & & \multicolumn{3}{|c|}{RSS (MB)} \\
|
|
||||||
\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
|
|
||||||
konfigurierten Cache Einstellungen.
|
|
||||||
|
|
||||||
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 Speicher
|
|
||||||
der Anwendung beobachtet, sondern die Speichernutzung des Docker-Containers für den Payara-Server.
|
|
||||||
|
|
||||||
\subsection{Caching im OpenJPA}
|
|
||||||
\label{sec:performance-checking: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
|
|
||||||
Objekte aus dem Cache entfernt und in eine SoftReferenceMap übertragen.
|
|
||||||
|
|
||||||
Zuerst wird mit aktivierten Cache mit einer Cache-Größe von 1000 Elemente getestet. Wie in \ref{tbl:measure-ojpa-active}
|
|
||||||
zu sehen, dauert auch hier der erste Aufruf minimal länger als ohne akiviertem Cache. Alle Nachfolgenden Aufrufe
|
|
||||||
wiederrum sind um 100ms schneller in der Verarbeitung. Auch bei der Anzahl der Anfragen an die Datenbank kann mehr den
|
|
||||||
Rückgang der Anfragen sehr gut sehen. Aktuell kann die Verringerung des wachsenden Speicherbedarfs nur nicht erklärt
|
|
||||||
werden.
|
|
||||||
|
|
||||||
\begin{table}[h!]
|
|
||||||
\centering
|
|
||||||
\begin{tabular}{|r|r|r|r|r|r|r|r|}
|
|
||||||
\hline
|
|
||||||
& \multicolumn{3}{|c|}{Aufrufzeit (ms)} & & \multicolumn{3}{|c|}{RSS (MB)} \\
|
|
||||||
\hline
|
|
||||||
\# & min & avg & max & Queries & davor & danach & diff \\
|
|
||||||
\hline
|
|
||||||
1 & 277 & 469 & 1506 & 7206 & 764,21 & 859.96 & 95.75 \\
|
|
||||||
2 & 228 & 269 & 384 & 6767 & 848,64 & 908,44 & 59.80 \\
|
|
||||||
3 & 224 & 238 & 299 & 6656 & 898.71 & 949.94 & 51.23 \\
|
|
||||||
4 & 214 & 235 & 325 & 6671 & 936.70 & 999.49 & 62.79 \\
|
|
||||||
\hline
|
|
||||||
\end{tabular}
|
|
||||||
\caption{Messung mit OpenJPA-Cache und Größe auf 1000}
|
|
||||||
\label{tbl:measure-ojpa-active}
|
|
||||||
\end{table}
|
|
||||||
|
|
||||||
Bei einer erhöhten Cache-Größe, zeigt sich auf den ersten Blick ein noch besseres Bild ab, wie in
|
|
||||||
\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
|
|
||||||
\begin{tabular}{|r|r|r|r|r|r|r|r|}
|
|
||||||
\hline
|
|
||||||
& \multicolumn{3}{|c|}{Aufrufzeit (ms)} & & \multicolumn{3}{|c|}{RSS (MB)} \\
|
|
||||||
\hline
|
|
||||||
\# & min & avg & max & Queries & davor & danach & diff \\
|
|
||||||
\hline
|
|
||||||
1 & 178 & 347 & 1507 & 1419 & 752.10 & 862.38 & 110,28 \\
|
|
||||||
2 & 126 & 152 & 232 & 60 & 853.72 & 875.21 & 21.49 \\
|
|
||||||
3 & 130 & 134 & 142 & 60 & 880.08 & 880.94 & 0,86 \\
|
|
||||||
4 & 125 & 128 & 135 & 60 & 865.36 & 897.96 & 32.60 \\
|
|
||||||
\hline
|
|
||||||
\end{tabular}
|
|
||||||
\caption{Messung mit OpenJPA-Cache und Größe auf 10000}
|
|
||||||
\label{tbl:measure-ojpa-active-bigger}
|
|
||||||
\end{table}
|
|
||||||
|
|
||||||
\mytodos{pin und unpin noch mit einbringen? SoftReferenceMap nochmal genau durchleuchte, laut doku entfällt dort nichts
|
|
||||||
wenn kein Timeout auf der Klasse definiert ist}
|
|
||||||
\mytodos{kurzes Fazit fehlt noch!}
|
|
||||||
|
|
||||||
\subsection{Caching im \ac{JPA}}
|
|
||||||
\label{sec:performance-checking:investigation-application:caching-jpa}
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
Um den Cache zu deaktivieren wurden beiden Einstellungen auf \textit{false} gestellt, die Größe auf 0 und der Cache-Typ
|
|
||||||
auf \textit{NONE}. Hierbei lag die maximale gemessene Laufzeit des ersten Aufrufs bei ca. 1300 ms und es wurden 12219
|
|
||||||
Abfragen an die Datenbank gestellt. Bei den nachfolgenden Aufrufe lag die Aufrufzeit im Durchschnitt bei 350 ms und
|
|
||||||
12080 Abfragen.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
Bei WEAK hat sich die Speichernutzung nur um 5MB gesteigert
|
|
||||||
|
|
||||||
\mytodos{in einer Tabelle oder Graphen darstellen?}
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
\subsection{Caching in \ac{EJB}}
|
|
||||||
\label{sec:performance-checking: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
|
|
||||||
\begin{tabular}{|r|r|r|r|r|r|r|r|}
|
|
||||||
\hline
|
|
||||||
& \multicolumn{3}{|c|}{Aufrufzeit (ms)} & & \multicolumn{3}{|c|}{RSS (MB)} \\
|
|
||||||
\hline
|
|
||||||
\# & min & avg & max & Queries & davor & danach & diff \\
|
|
||||||
\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}
|
|
||||||
\caption{Messung mit \ac{EJB}-Cache}
|
|
||||||
\label{tbl:measure-ejb-cache-active}
|
|
||||||
\end{table}
|
|
||||||
|
|
||||||
\subsection{Abfragen \ac{JPQL}}
|
|
||||||
\label{sec:performance-checking: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}
|
|
||||||
referenziert. In eingriff in die Abfrage ist hier leider nicht möglich, wie man im Code \ref{lst:jpql-document-list}
|
|
||||||
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}
|
|
||||||
|
|
||||||
Da dieser Code direkt so aus dem Projekt kommt, wird hierfür keine gesonderte Zeitmessung durchgeführt, da dies durch
|
|
||||||
\ref{tbl:measure-without-cache} geschehen ist.
|
|
||||||
|
|
||||||
\subsection{Abfragen Criteria API}
|
|
||||||
\label{sec:performance-checking: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
|
|
||||||
\begin{tabular}{|r|r|r|r|r|r|r|r|}
|
|
||||||
\hline
|
|
||||||
& \multicolumn{3}{|c|}{Aufrufzeit (ms)} & & \multicolumn{3}{|c|}{RSS (MB)} \\
|
|
||||||
\hline
|
|
||||||
\# & min & avg & max & Queries & davor & danach & diff \\
|
|
||||||
\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}
|
|
||||||
\caption{Messung mit Criteria-API ohne Cache}
|
|
||||||
\label{tbl:measure-criteria-api}
|
|
||||||
\end{table}
|
|
||||||
|
|
||||||
|
|
||||||
\subsection{materialized views}
|
|
||||||
\label{sec:performance-checking: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
|
|
||||||
läuft der Datenbestand der Sicht und der zugrundeliegenden Abfrage auseinander.
|
|
||||||
|
|
||||||
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}
|
|
||||||
|
|
||||||
\begin{table}[h!]
|
|
||||||
\centering
|
|
||||||
\begin{tabular}{|r|r|r|r|r|r|r|r|}
|
|
||||||
\hline
|
|
||||||
& \multicolumn{3}{|c|}{Aufrufzeit (ms)} & & \multicolumn{3}{|c|}{RSS (MB)} \\
|
|
||||||
\hline
|
|
||||||
\# & min & avg & max & Queries & davor & danach & diff \\
|
|
||||||
\hline
|
|
||||||
1 & 364 & 472 & 1225 & 306 & 821.03 & 890.15 & xxx.xx \\
|
|
||||||
2 & 345 & 361 & 290 & 100 & 839.89 & 852.26 & xxx.xx \\
|
|
||||||
3 & xxx & xxx & xxx & xxxxx & xxxx.xx & xxxx.xx & xxx.xx \\
|
|
||||||
4 & xxx & xxx & xxx & xxxxx & xxxx.xx & xxxx.xx & xxx.xx \\
|
|
||||||
\hline
|
|
||||||
\end{tabular}
|
|
||||||
\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
|
|
||||||
Speicheranstieg weniger stark aus.
|
|
||||||
|
|
||||||
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}
|
|
||||||
|
|
||||||
\mytodos{Die Indizies noch mit aufnehmen!}
|
|
||||||
|
|
||||||
Nach dem Anpassungen haben sich dann die Werte aus \ref{tbl:measure-materialized-view-ext} ergeben.
|
|
||||||
|
|
||||||
\begin{table}[h!]
|
|
||||||
\centering
|
|
||||||
\begin{tabular}{|r|r|r|r|r|r|r|r|}
|
|
||||||
\hline
|
|
||||||
& \multicolumn{3}{|c|}{Aufrufzeit (ms)} & & \multicolumn{3}{|c|}{RSS (MB)} \\
|
|
||||||
\hline
|
|
||||||
\# & min & avg & max & Queries & davor & danach & diff \\
|
|
||||||
\hline
|
|
||||||
1 & 348 & 419 & 869 & 178 & 792.11 & 846.29 & 54.18 \\
|
|
||||||
2 & 340 & 347 & 367 & 90 & 810.77 & 832.57 & 21.80 \\
|
|
||||||
3 & 296 & 353 & 491 & 90 & 840.39 & 867.92 & 27.53 \\
|
|
||||||
4 & 294 & 315 & 392 & 90 & 876.19 & 885.31 & 9.12 \\
|
|
||||||
\hline
|
|
||||||
\end{tabular}
|
|
||||||
\caption{Messung mit erweiterter Materialized View}
|
|
||||||
\label{tbl:measure-materialized-view-ext}
|
|
||||||
\end{table}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
\subsection{cached queries}
|
|
||||||
\label{sec:performance-checking: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.
|
|
||||||
|
|
||||||
\begin{table}[h!]
|
|
||||||
\centering
|
|
||||||
\begin{tabular}{|r|r|r|r|r|r|r|r|}
|
|
||||||
\hline
|
|
||||||
& \multicolumn{3}{|c|}{Aufrufzeit (ms)} & & \multicolumn{3}{|c|}{RSS (MB)} \\
|
|
||||||
\hline
|
|
||||||
\# & min & avg & max & Queries & davor & danach & diff \\
|
|
||||||
\hline
|
|
||||||
1 & 391 & 593 & 1533 & 12256 & 843.63 & 1009.79 & 116.16 \\
|
|
||||||
2 & 281 & 365 & 584 & 12080 & 996.28 & 1114.60 & 118.32 \\
|
|
||||||
3 & 295 & 353 & 464 & 12080 & 1103.30 & 1201.47 & 98.17 \\
|
|
||||||
4 & 280 & 292 & 324 & 12080 & 1191.56 & 1298.46 & 106.90 \\
|
|
||||||
\hline
|
|
||||||
\end{tabular}
|
|
||||||
\caption{Messung mit aktiviertem Cached Queries}
|
|
||||||
\label{tbl:measure-cached-queries}
|
|
||||||
\end{table}
|
|
||||||
|
|
||||||
\subsection{Umgestalten der Datenbanktabellen}
|
|
||||||
\label{sec:performance-checking:investigation-application:new-table}
|
|
||||||
|
|
||||||
\subsection{Verkleinerung der Abfragen}
|
|
||||||
\label{sec:performance-checking:investigation-application:smaller-query}
|
|
||||||
|
|
||||||
\subsection{Statische Webseiten}
|
|
||||||
\label{sec:performance-checking:investigation-application:static-website}
|
|
||||||
|
|
||||||
Wenn man die Dokumentenliste als statische Webseiten ablegt, werden die Zugriffszeiten sehr kurz sein. Darüber hinaus
|
|
||||||
funktionieren in statische Webseiten aber keine Suche oder eine Sortierung. Sonst müsste man für jede mögliche
|
|
||||||
Sortierung und Suchanfrage einen Satz der Dokumentenliste als statische Webseite bereitstellen. Für die Sortierungen
|
|
||||||
wäre das noch möglich, aber für die Suchanfragen ist dies nicht mehr möglich. Daher ist die Umstellung auf statische
|
|
||||||
Webseiten nicht sinnvoll.
|
|
||||||
|
|
||||||
\mytodos{docker-file und bsopt in den Anhang packen}
|
|
|
@ -1,22 +1,518 @@
|
||||||
% !TeX root = ../../thesis.tex
|
% !TeX root = ../../thesis.tex
|
||||||
|
|
||||||
\chapter{???Optimierung???}
|
\chapter{Performance-Untersuchung der Anwendung}
|
||||||
\label{ch:optimizing}
|
\label{ch:performance-investigation-application}
|
||||||
|
|
||||||
\mytodos{Muss noch entsprechend der Auswertungen aus der Performance-Untersuchungen angeapasst werden}
|
Nun werden die unterschiedlichen Schichten betrachtet und möglichen Performance-Verbesserungen untersucht und deren
|
||||||
|
Vor"= und Nachteile herausgearbeitet.
|
||||||
|
|
||||||
\section{Ermittlung der Performance-Probleme}
|
Für die Tests wird ein aktuelles Manjaro-System mit frisch installierten Payara als Serverhost und der IntelliJ IDEA
|
||||||
\label{sec:optimizing:performance}
|
als Entwicklungsumgebung verwendet. Der Computer ist mit einer Intel CPU i7-12700K, 32 GB Arbeitsspeicher und einer SSD
|
||||||
|
als Systemfestplatte ausgestattet.
|
||||||
|
|
||||||
\section{Analyse der Abfrage}
|
Zur ersten Untersuchung und der Bestimmung der Basis-Linie, wurde das Script ohne eine Änderung an dem Code und der
|
||||||
\label{sec:optimizing:query-analyse}
|
Konfiguration mehrfach aufgerufen. Hierbei hat sich gezeigt, dass der erste Aufruf nach dem Deployment ca. 1500 ms
|
||||||
|
gedauert hat. Die weiteren Aufrufe dauert dann im Durchschnitt bei 600 ms. Beim achten Aufruf des Scripts hat der
|
||||||
|
Server nicht mehr reagiert und im Log ist ein OutOfMemoryError protokolliert worden.
|
||||||
|
|
||||||
\section{Optimierungen der Abfragen}
|
Nach einem Neustart des Servers, konnte das gleiche Verhalten wieder reproduziert werden. Daraufhin wurde das Test-Script
|
||||||
\label{sec:optimizing:query-optimizing}
|
um die Anzeige der aktuellen Speicherverwendung des Payara-Servers erweitert und diese zeitgleich zu beobachten. Diese
|
||||||
|
Auswertung zeigte, dass der Server mit ca. 1500 MB RSS Nutzung an seine Grenzen stößt. Diese Grenzen wurde durch die
|
||||||
|
Konfigurationsänderung im Payara-Server von \texttt{-Xmx512m} auf \texttt{-Xmx4096m} nach oben verschoben. Nun werden
|
||||||
|
ca. 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 ca. 4700 MB RSS. Bei allen Tests war noch mehr als die
|
||||||
|
Hälfte des verfügbaren Arbeitsspeichers unbenutzt.
|
||||||
|
|
||||||
\section{Anpassung der Konfiguration}
|
Dies zeigt direkt, dass es ein problem in der Freigabe der Objekte gibt, da dass erhöhen des verwendbaren Arbeitsspeicher
|
||||||
\label{sec:optimizing:configuration}
|
das Problem nicht löst, sondern nur verschiebt.
|
||||||
|
|
||||||
und hier ein sql-beispiel \autoref{lst:tester}
|
Als Grundlage für die Vergleiche wurden eine Messung durchgeführt, bei der alle Caches deaktiviert wurden und keine
|
||||||
\includecode[SQL]{chapters/thesis/chapter05_example.sql}{lst:tester}{ein sql beispiel}
|
Ä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.
|
||||||
|
|
||||||
|
\mytodos{hier noch text einfügen, der erklärt wie die Spalten zu werten sind, also Aufrufzeit ist kürzer gleich besser}
|
||||||
|
|
||||||
|
\begin{table}[h!]
|
||||||
|
\centering
|
||||||
|
\begin{tabular}{|r|r|r|r|r|r|r|r|}
|
||||||
|
\hline
|
||||||
|
& \multicolumn{3}{|c|}{Aufrufzeit (ms)} & & \multicolumn{3}{|c|}{RSS (MB)} \\
|
||||||
|
\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}
|
||||||
|
|
||||||
|
\mytodos{hier noch beschreiben, wie die Werte zu interpretieren sind, und hervorheben, dass dies nun für alle Tabellen so gilt!}
|
||||||
|
|
||||||
|
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
|
||||||
|
konfigurierten Cache Einstellungen.
|
||||||
|
|
||||||
|
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
|
||||||
|
Speicher der Anwendung beobachtet, sondern die Speichernutzung des Docker-Containers für den Payara-Server. Für die
|
||||||
|
Ausführungszeiten der SQL-Abfragen wurden nur die 2 Hauptabfragen auf der Document-Tabelle, die Abfrage der 400
|
||||||
|
Dokumente, sowie den letzten und ersten Eintrag der Tabelle.
|
||||||
|
\mytodos{es müssen die 6 Anfragen sein, documentaddresseeperson, documentcoauthorperson, documentfacsimile und count}
|
||||||
|
|
||||||
|
\begin{table}[h!]
|
||||||
|
\centering
|
||||||
|
\begin{tabular}{|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)} \\
|
||||||
|
\hline
|
||||||
|
\# & min & avg & max & cnt & sum & davor & danach & diff \\
|
||||||
|
\hline
|
||||||
|
1 & 354 & 649 & 2225 & 12240 & 181 & 967 & 1004 & 37 \\
|
||||||
|
2 & 288 & 328 & 409 & 12080 & 175 & 1004 & 1113 & 109 \\ % 356ms
|
||||||
|
3 & 294 & 449 & 746 & 12080 & 177 & 1113 & 1258 & 145 \\ % 533ms
|
||||||
|
4 & 289 & 371 & 634 & 12080 & 180 & 1279 & 1541 & 262 \\ % 713ms
|
||||||
|
\hline
|
||||||
|
\end{tabular}
|
||||||
|
\caption{Messung ohne Caches im Docker}
|
||||||
|
\label{tbl:measure-without-cache-docker}
|
||||||
|
\end{table}
|
||||||
|
|
||||||
|
\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
|
||||||
|
Objekte aus dem Cache entfernt und in eine SoftReferenceMap übertragen.
|
||||||
|
|
||||||
|
Zuerst wird mit aktivierten Cache mit einer Cache-Größe von 1000 Elemente getestet. Wie in \ref{tbl:measure-ojpa-active}
|
||||||
|
zu sehen, dauert auch hier der erste Aufruf minimal länger als ohne akiviertem Cache. Alle Nachfolgenden Aufrufe
|
||||||
|
wiederrum sind um 100ms schneller in der Verarbeitung. Auch bei der Anzahl der Anfragen an die Datenbank kann mehr den
|
||||||
|
Rückgang der Anfragen sehr gut sehen. Aktuell kann die Verringerung des wachsenden Speicherbedarfs nur nicht erklärt
|
||||||
|
werden.
|
||||||
|
|
||||||
|
\begin{table}[h!]
|
||||||
|
\centering
|
||||||
|
\begin{tabular}{|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)} \\
|
||||||
|
\hline
|
||||||
|
\# & min & avg & max & cnt & sum & davor & danach & diff \\
|
||||||
|
\hline
|
||||||
|
1 & 277 & 469 & 1506 & 7206 & 183 & 764,21 & 859.96 & 95.75 \\ % 183ms
|
||||||
|
2 & 228 & 269 & 384 & 6767 & 173 & 848,64 & 908,44 & 59.80 \\ % 356ms
|
||||||
|
3 & 224 & 238 & 299 & 6656 & 180 & 898.71 & 949.94 & 51.23 \\ % 535ms
|
||||||
|
4 & 214 & 235 & 325 & 6671 & 179 & 936.70 & 999.49 & 62.79 \\ % 714ms
|
||||||
|
\hline
|
||||||
|
\end{tabular}
|
||||||
|
\caption{Messung mit OpenJPA-Cache und Größe auf 1000}
|
||||||
|
\label{tbl:measure-ojpa-active}
|
||||||
|
\end{table}
|
||||||
|
|
||||||
|
Bei einer erhöhten Cache-Größe, zeigt sich auf den ersten Blick ein noch besseres Bild ab, wie in
|
||||||
|
\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
|
||||||
|
\begin{tabular}{|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)} \\
|
||||||
|
\hline
|
||||||
|
\# & min & avg & max & cnt & sum & davor & danach & diff \\
|
||||||
|
\hline
|
||||||
|
1 & 178 & 347 & 1507 & 1419 & 214 & 752.10 & 862.38 & 110,28 \\ % 214ms
|
||||||
|
2 & 126 & 152 & 232 & 60 & 168 & 853.72 & 875.21 & 21.49 \\ % 382ms
|
||||||
|
3 & 130 & 134 & 142 & 60 & 172 & 880.08 & 880.94 & 0,86 \\ % 554ms
|
||||||
|
4 & 125 & 128 & 135 & 60 & 177 & 865.36 & 897.96 & 32.60 \\ % 731ms
|
||||||
|
\hline
|
||||||
|
\end{tabular}
|
||||||
|
\caption{Messung mit OpenJPA-Cache und Größe auf 10000}
|
||||||
|
\label{tbl:measure-ojpa-active-bigger}
|
||||||
|
\end{table}
|
||||||
|
|
||||||
|
\mytodos{pin und unpin noch mit einbringen? SoftReferenceMap nochmal genau durchleuchte, laut doku entfällt dort nichts
|
||||||
|
wenn kein Timeout auf der Klasse definiert ist}
|
||||||
|
\mytodos{kurzes Fazit fehlt noch, anzahl der Queries für unterabfragen gehen auf 0 bzw. bleiben bei 400!}
|
||||||
|
|
||||||
|
\section{Caching im \ac{JPA}}
|
||||||
|
\label{sec:performance-investigation-application:caching-jpa}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
Um den Cache zu deaktivieren wurden beiden Einstellungen auf \textit{false} gestellt, die Größe auf 0 und der Cache-Typ
|
||||||
|
auf \textit{NONE}. Hierbei lag die maximale gemessene Laufzeit des ersten Aufrufs bei ca. 1300 ms und es wurden 12219
|
||||||
|
Abfragen an die Datenbank gestellt. Bei den nachfolgenden Aufrufe lag die Aufrufzeit im Durchschnitt bei 350 ms und
|
||||||
|
12080 Abfragen.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Bei WEAK hat sich die Speichernutzung nur um 5MB gesteigert
|
||||||
|
|
||||||
|
\mytodos{in einer Tabelle oder Graphen darstellen?}
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
\section{Caching in \ac{EJB}}
|
||||||
|
\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
|
||||||
|
\begin{tabular}{|r|r|r|r|r|r|r|r|}
|
||||||
|
\hline
|
||||||
|
& \multicolumn{3}{|c|}{Aufrufzeit (ms)} & & \multicolumn{3}{|c|}{RSS (MB)} \\
|
||||||
|
\hline
|
||||||
|
\# & min & avg & max & Queries & davor & danach & diff \\
|
||||||
|
\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}
|
||||||
|
\caption{Messung mit \ac{EJB}-Cache}
|
||||||
|
\label{tbl:measure-ejb-cache-active}
|
||||||
|
\end{table}
|
||||||
|
|
||||||
|
\section{Abfragen \ac{JPQL}}
|
||||||
|
\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}
|
||||||
|
referenziert. In eingriff in die Abfrage ist hier leider nicht möglich, wie man im Code \ref{lst:jpql-document-list}
|
||||||
|
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}
|
||||||
|
|
||||||
|
Da dieser Code direkt so aus dem Projekt kommt, wird hierfür keine gesonderte Zeitmessung durchgeführt, da dies durch
|
||||||
|
\ref{tbl:measure-without-cache} geschehen ist.
|
||||||
|
|
||||||
|
\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
|
||||||
|
\begin{tabular}{|r|r|r|r|r|r|r|r|}
|
||||||
|
\hline
|
||||||
|
& \multicolumn{3}{|c|}{Aufrufzeit (ms)} & & \multicolumn{3}{|c|}{RSS (MB)} \\
|
||||||
|
\hline
|
||||||
|
\# & min & avg & max & Queries & davor & danach & diff \\
|
||||||
|
\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}
|
||||||
|
\caption{Messung mit Criteria-API ohne Cache}
|
||||||
|
\label{tbl:measure-criteria-api}
|
||||||
|
\end{table}
|
||||||
|
|
||||||
|
|
||||||
|
\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
|
||||||
|
läuft der Datenbestand der Sicht und der zugrundeliegenden Abfrage auseinander.
|
||||||
|
|
||||||
|
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}
|
||||||
|
|
||||||
|
\begin{table}[h!]
|
||||||
|
\centering
|
||||||
|
\begin{tabular}{|r|r|r|r|r|r|r|r|}
|
||||||
|
\hline
|
||||||
|
& \multicolumn{3}{|c|}{Aufrufzeit (ms)} & & \multicolumn{3}{|c|}{RSS (MB)} \\
|
||||||
|
\hline
|
||||||
|
\# & min & avg & max & Queries & davor & danach & diff \\
|
||||||
|
\hline
|
||||||
|
1 & 364 & 472 & 1225 & 306 & 821.03 & 890.15 & xxx.xx \\
|
||||||
|
2 & 345 & 361 & 290 & 100 & 839.89 & 852.26 & xxx.xx \\
|
||||||
|
3 & xxx & xxx & xxx & xxxxx & xxxx.xx & xxxx.xx & xxx.xx \\
|
||||||
|
4 & xxx & xxx & xxx & xxxxx & xxxx.xx & xxxx.xx & xxx.xx \\
|
||||||
|
\hline
|
||||||
|
\end{tabular}
|
||||||
|
\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
|
||||||
|
Speicheranstieg weniger stark aus.
|
||||||
|
|
||||||
|
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}
|
||||||
|
|
||||||
|
\mytodos{Die Indizies noch mit aufnehmen!}
|
||||||
|
|
||||||
|
Nach dem Anpassungen haben sich dann die Werte aus \ref{tbl:measure-materialized-view-ext} ergeben.
|
||||||
|
|
||||||
|
\begin{table}[h!]
|
||||||
|
\centering
|
||||||
|
\begin{tabular}{|r|r|r|r|r|r|r|r|}
|
||||||
|
\hline
|
||||||
|
& \multicolumn{3}{|c|}{Aufrufzeit (ms)} & & \multicolumn{3}{|c|}{RSS (MB)} \\
|
||||||
|
\hline
|
||||||
|
\# & min & avg & max & Queries & davor & danach & diff \\
|
||||||
|
\hline
|
||||||
|
1 & 348 & 419 & 869 & 178 & 792.11 & 846.29 & 54.18 \\
|
||||||
|
2 & 340 & 347 & 367 & 90 & 810.77 & 832.57 & 21.80 \\
|
||||||
|
3 & 296 & 353 & 491 & 90 & 840.39 & 867.92 & 27.53 \\
|
||||||
|
4 & 294 & 315 & 392 & 90 & 876.19 & 885.31 & 9.12 \\
|
||||||
|
\hline
|
||||||
|
\end{tabular}
|
||||||
|
\caption{Messung mit erweiterter Materialized View}
|
||||||
|
\label{tbl:measure-materialized-view-ext}
|
||||||
|
\end{table}
|
||||||
|
|
||||||
|
\mytodos{hier noch darauf eingehen, dass die Hauptarbeit nicht beim editieren sondern bei der Anzeige ist}
|
||||||
|
\mytodos{Das Render des Json in der View Betrachten, scheint der aktuelle Kostenpunkt zu sein}
|
||||||
|
\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{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.
|
||||||
|
|
||||||
|
\begin{table}[h!]
|
||||||
|
\centering
|
||||||
|
\begin{tabular}{|r|r|r|r|r|r|r|r|}
|
||||||
|
\hline
|
||||||
|
& \multicolumn{3}{|c|}{Aufrufzeit (ms)} & & \multicolumn{3}{|c|}{RSS (MB)} \\
|
||||||
|
\hline
|
||||||
|
\# & min & avg & max & Queries & davor & danach & diff \\
|
||||||
|
\hline
|
||||||
|
1 & 391 & 593 & 1533 & 12256 & 843.63 & 1009.79 & 116.16 \\
|
||||||
|
2 & 281 & 365 & 584 & 12080 & 996.28 & 1114.60 & 118.32 \\
|
||||||
|
3 & 295 & 353 & 464 & 12080 & 1103.30 & 1201.47 & 98.17 \\
|
||||||
|
4 & 280 & 292 & 324 & 12080 & 1191.56 & 1298.46 & 106.90 \\
|
||||||
|
\hline
|
||||||
|
\end{tabular}
|
||||||
|
\caption{Messung mit aktiviertem Cached Queries}
|
||||||
|
\label{tbl:measure-cached-queries}
|
||||||
|
\end{table}
|
||||||
|
|
||||||
|
\section{Umgestalten der Datenbanktabellen}
|
||||||
|
\label{sec:performance-investigation-application:new-table}
|
||||||
|
|
||||||
|
\mytodos{Erwähnen des Ansatz, von denormalisierung inkl. Grund warum es weg gelassen wurde}
|
||||||
|
|
||||||
|
\section{Verkleinerung der Abfragen}
|
||||||
|
\label{sec:performance-investigation-application:smaller-query}
|
||||||
|
|
||||||
|
\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
|
||||||
|
funktionieren in statische Webseiten aber keine Suche oder eine Sortierung. Sonst müsste man für jede mögliche
|
||||||
|
Sortierung und Suchanfrage einen Satz der Dokumentenliste als statische Webseite bereitstellen. Für die Sortierungen
|
||||||
|
wäre das noch möglich, aber für die Suchanfragen ist dies nicht mehr möglich. Daher ist die Umstellung auf statische
|
||||||
|
Webseiten nicht sinnvoll.
|
||||||
|
|
||||||
|
\mytodos{Hier noch explizirter definieren, dass die sortierten Daten als statische Seiten abgelegt werden}
|
||||||
|
|
||||||
|
\section{Client basierte Webseiten}
|
||||||
|
\label{sec:performance-investigation-application:client-side-rendering}
|
||||||
|
|
||||||
|
\mytodos{Beschreiben, dass alles auf dem client geschickt wird, und dort alles gerendert und sortiert wird,
|
||||||
|
damit aber schwächere Clients ausgeschlossen werden!
|
||||||
|
Hätte aber Vorteil für die Kosten des Servers, da dieser schwächer ausgelegt werden könnte und damit Geld einzusparen
|
||||||
|
wäre. !!!! Der Punkt könnte auch unter QueryCache gelagert werden !!!!}
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
select *
|
|
||||||
from tblCPDataX
|
|
||||||
where szName = N'EDA01'
|
|
Binary file not shown.
|
@ -95,6 +95,9 @@
|
||||||
--- Untersuchung vorher ---
|
--- Untersuchung vorher ---
|
||||||
\end{frame}
|
\end{frame}
|
||||||
|
|
||||||
|
% Hier 2-3 der aktuellen erarbeiten Ansätze Vorstellen und nach dem Warum fragen
|
||||||
|
% Genau beschreiben was Signifikant besser/schlechter ist
|
||||||
|
|
||||||
\begin{frame}
|
\begin{frame}
|
||||||
\frametitle{Caching mit OpenJPA}
|
\frametitle{Caching mit OpenJPA}
|
||||||
--- OpenJPA ---
|
--- OpenJPA ---
|
||||||
|
|
BIN
thesis.pdf
BIN
thesis.pdf
Binary file not shown.
Loading…
Reference in a new issue