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
|
||||
url_arr=(
|
||||
"$hostname/index.xhtml"
|
||||
"$hostname/view/document/list.xhtml"
|
||||
#"$hostname/view/document/listsearch.xhtml"
|
||||
#"$hostname/view/document/list.xhtml"
|
||||
"$hostname/view/document/listsearch.xhtml"
|
||||
#"$hostname/view/correspondent/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
|
||||
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}
|
||||
|
||||
\begin{lstlisting}[language=Bash,caption={Aufrufe des Unterstützungsscriptes},label=lst:calling_script_exec]
|
||||
callscript.sh measinit
|
||||
callscript.sh measres
|
||||
callscript.sh meascall
|
||||
callscript.sh meascall
|
||||
callscript.sh meascall
|
||||
callscript.sh meascall
|
||||
callscript.sh -rppf=_testname measres
|
||||
callscript.sh -rppf=_testname meascall
|
||||
callscript.sh -rppf=_testname -rppn=2 meascall
|
||||
callscript.sh -rppf=_testname -rppn=3 meascall
|
||||
callscript.sh -rppf=_testname -rppn=4 meascall
|
||||
\end{lstlisting}
|
|
@ -11,6 +11,7 @@ domain_log="$payara_path/logs/server.log"
|
|||
script_path="/opt/docker/timing.sh"
|
||||
pgbadger_out="/opt/docker/pgreport"
|
||||
report_postfix=""
|
||||
report_postno=""
|
||||
docker_name=dcpgbatch
|
||||
|
||||
COMPOSE_FILE=/opt/docker/docker-compose.yaml
|
||||
|
@ -54,13 +55,18 @@ pgconf() {
|
|||
}
|
||||
pgrp() {
|
||||
mkdir -p $pgbadger_out$report_postfix
|
||||
mkdir -p $pgbadger_out$report_postfix$report_postno
|
||||
outPath=$pgbadger_out$report_postfix/bash.out
|
||||
touch "$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() {
|
||||
rm -R $pgbadger_out$report_postfix
|
||||
if ["$report_postfix" -eq ""]; then
|
||||
rm -R $pgbadger_out
|
||||
else
|
||||
rm -R $pgbadger_out$report_postfix*
|
||||
fi
|
||||
}
|
||||
dccreate() {
|
||||
sudo docker compose -f $COMPOSE_FILE create --force-recreate
|
||||
|
@ -78,13 +84,12 @@ dcstats() {
|
|||
}
|
||||
for name in "$@"; do
|
||||
case $name in
|
||||
--rppf=*) report_postfix="${1#*=}" ;;
|
||||
-rppf=*) report_postfix="${name#*=}" ;;
|
||||
-rppn=*) report_postno="${name#*=}" ;;
|
||||
gflog) gflog ;;
|
||||
gfconf) gfconf ;;
|
||||
gfscript) gfscript ;;
|
||||
gfrestart)
|
||||
pgrpinit
|
||||
;;
|
||||
gfrestart) pgrpinit ;;
|
||||
pginit) pginit ;;
|
||||
pglogls) pglogls ;;
|
||||
pglogrm) pglogrm ;;
|
||||
|
@ -98,6 +103,7 @@ for name in "$@"; do
|
|||
dcstop) dcstop ;;
|
||||
dcstats) dcstats ;;
|
||||
measinit)
|
||||
pginit
|
||||
pgrpres
|
||||
pglogrm 0
|
||||
dccreate
|
||||
|
@ -124,7 +130,8 @@ for name in "$@"; do
|
|||
echo "ATTENTION: parameter must be defined in front of the commands!"
|
||||
echo ""
|
||||
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 "*** glassfish ***"
|
||||
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{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
|
||||
|
||||
\chapter{???Optimierung???}
|
||||
\label{ch:optimizing}
|
||||
\chapter{Performance-Untersuchung der Anwendung}
|
||||
\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}
|
||||
\label{sec:optimizing:performance}
|
||||
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.
|
||||
|
||||
\section{Analyse der Abfrage}
|
||||
\label{sec:optimizing:query-analyse}
|
||||
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.
|
||||
|
||||
\section{Optimierungen der Abfragen}
|
||||
\label{sec:optimizing:query-optimizing}
|
||||
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.
|
||||
|
||||
\section{Anpassung der Konfiguration}
|
||||
\label{sec:optimizing:configuration}
|
||||
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.
|
||||
|
||||
und hier ein sql-beispiel \autoref{lst:tester}
|
||||
\includecode[SQL]{chapters/thesis/chapter05_example.sql}{lst:tester}{ein sql beispiel}
|
||||
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}
|
||||
|
||||
\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 ---
|
||||
\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}
|
||||
\frametitle{Caching mit OpenJPA}
|
||||
--- OpenJPA ---
|
||||
|
|
BIN
thesis.pdf
BIN
thesis.pdf
Binary file not shown.
Loading…
Reference in a new issue