Es gibt 3 Hauptklassifikationen von langsamen Abfragen in AEM, die hier nach Schweregrad aufgelistet sind:
Abfragen ohne Index
Abfragen mit schlechter Einschränkung (oder schlechtem Bereich)
Abfragen mit vielen Ergebnissen
Die ersten beiden Klassifizierungen von Abfragen (index-los und schlecht eingeschränkt) sind langsam, da sie die Oak Abfrage Engine zwingen, jedes potenzielle Ergebnis (Inhaltsknoten oder Indexeintrag) zu überprüfen, um herauszufinden, welches in der Ergebnismenge IST enthalten ist.
Das Untersuchen aller potenziellen Ergebnisse wird als Durchlaufen bezeichnet.
Da jedes potenzielle Ergebnis überprüft werden muss, steigen die Kosten zur Bestimmung der tatsächlichen Ergebnismenge linear zur Anzahl der potenziellen Ergebnisse.
Durch Abfragebeschränkungen und Tuning von Indizes können die Indexdaten in einem optimierten Format gespeichert werden, das schnell Ergebnisse produziert und eine lineare Inspektion potenzieller Ergebnismengen unnötig macht.
In AEM 6.3 schlägt die Abfrage standardmäßig fehl und löst einen Ausnahmefehler aus, wenn 100.000 potenzielle Ergebnisse durchlaufen wurden. Diese Beschränkung ist in AEM Versionen vor AEM 6.3 nicht standardmäßig vorhanden, kann jedoch über die Apache Jackrabbit Abfrage Engine Settings OSGi-Konfiguration und QueryEngineSettings JMX Bean (Eigenschaft LimitReads) festgelegt werden.
Erklären Sie alle-Abfragen und stellen Sie sicher, dass ihre Abfragen nicht die /&ast enthalten; traverse Erklärung in ihnen. Beispiel für das Durchlaufen eines Abfrageplans:
[nt:unstructured] as [a] /* traverse "/content/*" where ([a].[unindexedProperty] = 'some value') and (isdescendantnode([a], [/content])) */
Überwachen Sie error.log
nach Index-losen Durchlaufabfragen:
*INFO* org.apache.jackrabbit.oak.query.QueryImpl Traversal query (query without index) ... ; consider creating and index
Rufen Sie die AEM Abfrage Performance Operations Console auf und Erläutern Sie langsame Abfragen, die nach Erklärungen zur horizontalen oder fehlenden Index-Abfrage suchen.
Erklären Sie alle Abfragen und stellen Sie sicher, dass sie auf einen Index aufgelöst werden, der den Eigenschaftsbeschränkungen der Abfrage entspricht.
indexRules
für alle Eigenschaftsbeschränkungen vorhanden, mindestens aber für die strengsten Eigenschaftsbeschränkungen in der Abfrage.orderable=true.
setzen.cqPageLucene
nicht über eine Indexregel für jcr:content/cq:tags
Vor dem Hinzufügen der cq:tags-Indexregel
cq:tags-Index-Regel
Query Builder-Abfrage
type=cq:Page
property=jcr:content/cq:tags
property.value=my:tag
Abfrageplan
[cq:Page] as [a] /* lucene:cqPageLucene(/oak:index/cqPageLucene?lang=de) *:* where [a].[jcr:content/cq:tags] = 'my:tag' */
Diese Abfrage wird auf den Index cqPageLucene
aufgelöst. Da jedoch keine Eigenschaftsindexregel für jcr:content
oder cq:tags
vorhanden ist, wird bei der Prüfung der Einschränkung jeder Datensatz im Index cqPageLucene
auf Übereinstimmung geprüft. Wenn also der Index 1 Million cq:Page
-Knoten enthält, werden 1 Million Datensätze geprüft, um die Ergebnismenge zu bestimmen.
Nach dem Hinzufügen der cq:tags-Indexregel
cq:tags-Index-Regel
/oak:index/cqPageLucene/indexRules/cq:Page/properties/cqTags
@name=jcr:content/cq:tags
@propertyIndex=true
Query Builder-Abfrage
type=cq:Page
property=jcr:content/cq:tags
property.value=myTagNamespace:myTag
Abfrageplan
[cq:Page] as [a] /* lucene:cqPageLucene(/oak:index/cqPageLucene?lang=de) jcr:content/cq:tags:my:tag where [a].[jcr:content/cq:tags] = 'my:tag' */
Durch das Hinzufügen der indexRule für jcr:content/cq:tags
im cqPageLucene
-Index können cq:tags
-Daten optimiert gespeichert werden.
Wenn eine Abfrage mit der Einschränkung jcr:content/cq:tags
ausgeführt wird, kann der Index die Ergebnisse nach Wert nachschlagen. Wenn also 100 cq:Page
-Knoten den Wert myTagNamespace:myTag
aufweisen, werden nur diese 100 Ergebnisse zurückgegeben. Die übrigen 999.000 werden aus den Einschränkungsprüfungen ausgeschlossen, was die Leistung um den Faktor 10.000 verbessert.
Selbstverständlich verringern weitere Abfragebeschränkungen die möglichen Ergebnismengen und führen zu weiterer Abfrageoptimierung.
Gleichermaßen würde ohne zusätzliche Indexregel für die cq:tags
-Eigenschaft auch eine Fulltext-Abfrage mit einer Beschränkung auf cq:tags
schlecht laufen, da Ergebnisse aus dem Index alle Fulltext-Übereinstimmungen zurückgeben würden. Die Beschränkung auf cq:tags würde danach gefiltert.
Eine weitere Ursache von Filtern nach dem Index sind Zugangssteuerungslisten, die oft bei der Entwicklung übergangen werden. Stellen Sie sicher, dass die Abfrage keine Pfade zurückgibt, die dem Benutzer nicht zugänglich sind. Dies kann meist durch eine bessere Inhaltsstruktur sowie durch Bereitstellung relevanter Pfadbeschränkungen bei der Abfrage realisiert werden.
Eine nützliche Methode, um festzustellen, ob der Lucene-Index viele Ergebnisse zurückgibt, um eine sehr kleine Untergruppe als Ergebnis der Abfrage zurückzugeben, besteht darin, DEBUG-Protokolle für org.apache.jackrabbit.oak.plugins.index.lucene.LucenePropertyIndex
zu aktivieren und zu sehen, wie viele Dokumente aus dem Index geladen werden. Die Anzahl der Ergebnisse sollte nicht zu weit unter der Anzahl der geladenen Dokumente liegen. Weitere Informationen finden Sie unter Protokollierung.
Überwachen Sie die error.log
auf übergreifende Abfragen:
*WARN* org.apache.jackrabbit.oak.spi.query.Cursors$TraversingCursor Traversed ### nodes ... consider creating an index or changing the query
Rufen Sie die AEM Abfrage Performance Operations Console und Langsame Abfragen auf, die nach Abfragen suchen, die keine Einschränkungen der Abfrage-Eigenschaft für Indexeigenschaftsregeln auflösen.
Legen Sie niedrige Schwellenwerte für oak.queryLimitInMemory (z. B. 10000) und oak.queryLimitReads (z. B. 5000) fest und optimieren Sie die ressourcenintensive Abfrage, wenn die UnsupportedOperationException-Ausnahme „The query read more than x nodes…“ auftritt.
Dies trägt zur Vermeidung ressourcenintensiver Abfragen bei (d. h. keine Sicherung durch einen Index oder Sicherung durch einen weniger abdeckenden Index). Beispielsweise führt eine Abfrage, die 1 Million Knoten liest, zu einer großen E/A-Menge – mit negativen Folgen für die Gesamtleistung der Anwendung. Jede Abfrage, die aufgrund eines überschrittenen Limits fehlschlägt, sollte also analysiert und optimiert werden.
Überwachen Sie die Protokolle auf Abfragen, die einen großen Node-Traversal- oder großen Heap-Speicherverbrauch auslösen: "
*WARN* ... java.lang.UnsupportedOperationException: The query read or traversed more than 100000 nodes. To avoid affecting other tasks, processing was stopped.
Überwachen Sie die Protokolle auf Abfragen, die einen hohen Heap-Speicherverbrauch auslösen:
*WARN* ... java.lang.UnsupportedOperationException: The query read more than 500000 nodes in memory. To avoid running out of memory, processing was stopped
Bei AEM 6.0-6.2-Versionen können Sie den Schwellenwert für die Node-Traversal mithilfe von JVM-Parametern im Skript "AEM Beginn"anpassen, um zu verhindern, dass große Abfragen die Umgebung überladen. Folgende Werte werden empfohlen:
-Doak.queryLimitInMemory=500000
-Doak.queryLimitReads=100000
In AEM 6.3 sind die beiden oben stehenden Parameter standardmäßig vorkonfiguriert und können über die OSGi QueryEngineSettings bearbeitet werden.
Weitere Informationen unter: https://jackrabbit.apache.org/oak/docs/query/query-engine.html#Slow_Queries_and_Read_Limits
Das Motto der Abfrageleistungsoptimierung in AEM lautet:
„Je mehr Einschränkungen, desto besser.“
Im Folgenden werden einige empfohlene Anpassungen zur Verbesserung der Abfrageleistung beschrieben. Passen Sie zunächst die Abfrage an (ein geringfügiger Eingriff) und anschließend, wenn nötig, die Index-Definitionen.
AEM unterstützt die folgenden Abfragesprachen:
Im folgenden Beispiel wird Query Builder verwendet, da es von AEM-Entwicklern am häufigsten verwendet wird. Die Prinzipien sind jedoch auch auf JCR-SQL2 und XPath anwendbar.
Nicht optimierte Abfrage
property=jcr:content/contentType
property.value=article-page
Optimierte Abfrage
type=cq:Page
property=jcr:content/contentType
property.value=article-page
Bei Abfragen ohne Knotentyp-Einschränkung muss AEM den nodetype nt:base
annehmen. Da jeder Knoten in AEM davon ein Untertyp ist, führt dies effektiv zu keiner Knotentyp-Einschränkung.
Durch das Festlegen von type=cq:Page
wird diese Abfrage auf nur cq:Page
-Knoten beschränkt und die Abfrage auf cqPageLucene AEM, wodurch die Ergebnisse auf eine Untergruppe von Knoten (nur cq:Page
-Knoten) in AEM beschränkt werden.
Nicht optimierte Abfrage
type=nt:hierarchyNode
property=jcr:content/contentType
property.value=article-page
Optimierte Abfrage
type=cq:Page
property=jcr:content/contentType
property.value=article-page
nt:hierarchyNode
ist der übergeordnete Knoten von cq:Page
und vorausgesetzt, jcr:content/contentType=article-page
dass diese Abfrage nur über unsere benutzerdefinierte Anwendung auf cq:Page
Knoten angewendet wird, gibt diese nur cq:Page
Knoten zurück, wo jcr:content/contentType=article-page
. Dies ist jedoch aus folgenden Gründen eine suboptimale Beschränkung:
nt:hierarchyNode
(z.B. dam:Asset
), die zu den möglichen Ergebnissen unnötigerweise hinzufügt.nt:hierarchyNode
gibt es keinen AEM bereitgestellten Index, jedoch gibt es einen bereitgestellten Index für cq:Page
.Wenn Sie type=cq:Page
setzen, wird die Abfrage auf cq:Page
-Knoten beschränkt und auf cqPageLucene von AEM aufgelöst. Dadurch werden die Ergebnisse auf eine Untergruppe von Knoten (nur cq:Page-Knoten) in AEM beschränkt.
Nicht optimierte Abfrage
property=jcr:content/contentType
property.value=article-page
Optimierte Abfrage
property=jcr:content/sling:resourceType
property.value=my-site/components/structure/article-page
Durch Ändern der Eigenschaftsbeschränkung von jcr:content/contentType
(benutzerdefinierter Wert) in die bekannte Eigenschaft sling:resourceType
kann die Abfrage in den Eigenschaftenindex slingResourceType
aufgelöst werden, der den gesamten Inhalt durch sling:resourceType
indiziert.
Eigenschaftsindizes (anstelle von Lucene-Eigenschaftsindizes) eignen sich am besten, wenn die Abfrage nicht nach Knotentyp unterscheidet und eine einzige Eigenschaftsbeschränkung die Ergebnismenge beherrscht.
/content/my-site/us/en
oder /content/my-site
oder /content/dam
anstelle von /
vor.Nicht optimierte Abfrage
type=cq:Page
path=/content
property=jcr:content/contentType
property.value=article-page
Optimierte Abfrage
type=cq:Page
path=/content/my-site/us/en
property=jcr:content/contentType
property.value=article-page
Durch das Kopieren der Pfadbeschränkung von path=/content
auf path=/content/my-site/us/en
können die Indizes die Anzahl der Indexeinträge verringern, die überprüft werden müssen. Wenn die Abfrage den Pfad sehr gut einschränken kann, über /content
oder /content/dam
hinaus, stellen Sie sicher, dass der Index evaluatePathRestrictions=true
hat.
Beachten Sie, dass evaluatePathRestrictions
die Indexgröße erhöht.
LIKE
und fn:XXXX
, da ihre Kosten mit der Anzahl der einschränkungsbasierten Ergebnisse skalieren.Nicht optimierte Abfrage
type=cq:Page
property=jcr:content/contentType
property.operation=like
property.value=%article%
Optimierte Abfrage
type=cq:Page
fulltext=article
fulltext.relPath=jcr:content/contentType
Die LIKE-Bedingung ist nur langsam auszuwerten, da kein Index verwendet werden kann, wenn der Text mit einem Platzhalter ("%…") Beginn. Die Bedingung jcr: ermöglicht die Verwendung eines Volltext-Index und wird daher bevorzugt. Dazu muss der aufgelöste Lucene-Eigenschaftsindex indexRule für jcr:content/contentType
mit analayzed=true
enthalten.
Die Verwendung von Abfrage-Funktionen wie fn:lowercase(..)
kann schwieriger zu optimieren sein, da es keine schnelleren Entsprechungen gibt (außerhalb komplexerer und obtrusiver Indexanalysatoren-Konfigurationen). Es ist ratsam, andere Scoping-Beschränkungen zu identifizieren, damit die Funktionen mit der kleinstmöglichen Ergebnismenge arbeiten, um die Abfrageleistung insgesamt zu verbessern.
Diese Anpassung ist nur im Query Builder möglich und gilt nicht für JCR-SQL2 oder XPath.
Verwenden Sie guessTotal in Query Builder, wenn die vollständige Ergebnismenge nicht sofort benötigt wird.
Nicht optimierte Abfrage
type=cq:Page
path=/content
Optimierte Abfrage
type=cq:Page
path=/content
p.guessTotal=100
Wenn die Abfrage schnell ausgeführt wird, aber sehr viele Ergebnisse zurückgibt, stellt p.guessTotal
eine wichtige Optimierung für Query Builder-Abfragen dar.
p.guessTotal=100
sorgt dafür, dass Query Builder nur die ersten 100 Ergebnisse erfasst, und setzt einen booleschen Wert, der angibt, ob mindestens ein weiteres Ergebnis vorliegt (jedoch nicht die Anzahl der weiteren Ergebnisse, da das Zählen den Vorgang verlangsamen würde). Diese Optimierung eignet sich hervorragend für Anwendungsfälle mit Paginierung oder endlosem Laden, wenn nur eine Teilmenge der Ergebnisse schrittweise angezeigt wird.
Wenn die optimale Abfrage auf einen Eigenschaftsindex aufgelöst wird, gibt es nichts mehr zu tun, da Eigenschaftsindizes nur minimale Anpassungsmöglichkeiten bieten.
Andernfalls sollte die Abfrage in einen Lucene-Eigenschaftenindex aufgelöst werden. Wenn kein Index aufgelöst werden kann, springen Sie zu „Erstellen eines neuen Index“.
Wandeln Sie bei Bedarf die Abfrage in XPath oder JCR-SQL2 um.
Query Builder-Abfrage
query type=cq:Page
path=/content/my-site/us/en
property=jcr:content/contentType
property.value=article-page
orderby=@jcr:content/publishDate
orderby.sort=desc
Aus der Query Builder-Abfrage generierter XPath
/jcr:root/content/my-site/us/en//element(*, cq:Page)[jcr:content/@contentType = 'article-page'] order by jcr:content/@publishDate descending
Stellen Sie den XPath (oder JCR-SQL2) dem Oak Index Definition Generator bereit, um die optimierte Lucene-Eigenschaftsindex-Definition zu generieren.
Generierte Lucene-Eigenschaftsindex-Definition
- evaluatePathRestrictions = true
- compatVersion = 2
- type = "lucene"
- async = "async"
- jcr:primaryType = oak:QueryIndexDefinition
+ indexRules
+ cq:Page
+ properties
+ contentType
- name = "jcr:content/contentType"
- propertyIndex = true
+ publishDate
- ordered = true
- name = "jcr:content/publishDate"
Fügen Sie die generierte Definition manuell in den vorhandenen Lucene-Eigenschaftenindex ein. Achten Sie darauf, dass Sie keine vorhandenen Konfigurationen entfernen, da sie zum Erfüllen anderer Abfragen verwendet werden können.
/oak:index/cqPageLucene
.Vergewissern Sie sich, dass die Abfrage nicht auf einen vorhandenen Lucene-Eigenschaftsindex aufgelöst wird. In diesem Fall finden Sie weitere Informationen im vorherigen Abschnitt zur Anpassung eines vorhandenen Index.
Wandeln Sie bei Bedarf die Abfrage in XPath oder JCR-SQL2 um.
Query Builder-Abfrage
type=myApp:Author
property=firstName
property.value=ira
Aus der Query Builder-Abfrage generierter XPath
//element(*, myApp:Page)[@firstName = 'ira']
Stellen Sie den XPath (oder JCR-SQL2) dem Oak Index Definition Generator bereit, um die optimierte Lucene-Eigenschaftsindex-Definition zu generieren.
Generierte Lucene-Eigenschaftsindex-Definition
- compatVersion = 2
- type = "lucene"
- async = "async"
- jcr:primaryType = oak:QueryIndexDefinition
+ indexRules
+ myApp:AuthorModel
+ properties
+ firstName
- name = "firstName"
- propertyIndex = true
Stellen Sie die generierte Lucene-Eigenschaftsindex-Definition bereit.
Fügen Sie die XML-Definition hinzu, die der Oak Index Definition Generator für den neuen Index für das AEM-Projekt, der Oak Index-Definitionen verwaltet, bereitgestellt hat. (Denken Sie daran, Oak Index-Definitionen als Code zu behandeln, da Code davon abhängt).
Stellen Sie den neuen Index bereit und testen Sie ihn entsprechend dem üblichen Lebenszyklus für AEM-Softwareentwicklung. Prüfen Sie, ob die Abfrage auf den Index aufgelöst wird und gute Leistung zeigt.
Nach der Erstbereitstellung dieses Index füllt AEM ihn mit den erforderlichen Daten aus.
Aufgrund der flexiblen Inhaltsarchitektur von AEM ist es schwer vorherzusagen und zu verhindern, dass der Durchlauf von Inhaltsstrukturen im Laufe der Zeit auf eine inakzeptable Größe anwächst.
Stellen Sie daher sicher, dass Indizes Abfragen erfüllen, es sei denn, die Kombination aus Pfadbeschränkung und Nodetypeinschränkung gewährleistet, dass weniger als 20 Knoten jemals durchlaufen werden.
Query Builder-Debugger
CRXDE Lite – Abfragewerkzeug
Query Builder-Protokollierung
DEBUG @ com.day.cq.search.impl.builder.QueryImpl
Oak Query-Ausführungsprotokollierung
DEBUG @ org.apache.jackrabbit.oak.query
Apache Jackrabbit Query Engine-Einstellungen – OSGi-Konfiguration
NodeCounter JMX Mbean
Oak Index Definition Generator