Esistono tre classificazioni principali delle query lente nell’AEM, elencate per gravità:
Query senza indice
Query con restrizioni (o ambito) insufficienti
Query di set di risultati di grandi dimensioni
Le prime due classificazioni di query (senza indice e con restrizioni insufficienti) sono lente. Sono lenti perché forzano il motore di query Oak a ispezionarle tutte potenziale risultato (nodo di contenuto o voce di indice) per identificare quale effettivo set di risultati.
L'atto di ispezionare ogni potenziale risultato è quello che è chiamato Traversing.
Poiché ogni risultato potenziale deve essere ispezionato, il costo per determinare il set di risultati effettivo aumenta in modo lineare con il numero di risultati potenziali.
L’aggiunta di restrizioni alle query e di indici di tuning consente di memorizzare i dati dell’indice in un formato ottimizzato che consente un rapido recupero dei risultati e, riduce o elimina la necessità di un’ispezione lineare dei potenziali set di risultati.
In AEM 6.3, per impostazione predefinita, quando viene raggiunto un attraversamento di 100.000, la query non riesce e viene generata un’eccezione. Questo limite non esiste per impostazione predefinita nelle versioni AEM precedenti alla AEM 6.3, ma può essere impostato tramite la configurazione OSGi Apache Jackrabbit Query Engine Settings e il bean JMX QueryEngineSettings (proprietà LimitReads).
Spiega tutto e assicurarsi che i relativi piani di query non contengano /* attraversare spiegazioni in essi. Esempio di piano di query di attraversamento:
[nt:unstructured] as [a] /* traverse "/content//*" where ([a].[unindexedProperty] = 'some value') and (isdescendantnode([a], [/content])) */
Monitorare error.log
per le query di attraversamento senza indice:
*INFO* org.apache.jackrabbit.oak.query.QueryImpl Traversal query (query without index) ... ; consider creating and index
Visita il AEM Prestazioni query console di gestione e Spiega query lente in cerca di spiegazioni di query trasversali o senza indicizzazione.
Spiega tutte le query e assicurati che vengano risolte in un indice sottoposto a tuning in modo che corrisponda alle restrizioni di proprietà della query.
indexRules
per tutte le restrizioni di proprietà e almeno per le restrizioni di proprietà più severe nella query.orderable=true.
cqPageLucene
non dispone di una regola di indice per jcr:content/cq:tags
Prima di aggiungere la regola di indice cq:tags
cq:tags Regola indice
Query di Query Builder
type=cq:Page
property=jcr:content/cq:tags
property.value=my:tag
Piano di query
[cq:Page] as [a] /* lucene:cqPageLucene(/oak:index/cqPageLucene?lang=it) *:* where [a].[jcr:content/cq:tags] = 'my:tag' */
Questa query viene risolta nel cqPageLucene
ma non esiste alcuna regola dell'indice di proprietà per jcr:content
o cq:tags
, quando questa restrizione viene valutata, ogni record nel cqPageLucene
L'indice è selezionato per determinare una corrispondenza. In quanto tale, se l’indice contiene 1 milione cq:Page
vengono quindi controllati 1 milione di record per determinare il set di risultati.
Dopo l’aggiunta della regola di indice cq:tags
cq:tags Regola indice
/oak:index/cqPageLucene/indexRules/cq:Page/properties/cqTags
@name=jcr:content/cq:tags
@propertyIndex=true
Query di Query Builder
type=cq:Page
property=jcr:content/cq:tags
property.value=myTagNamespace:myTag
Piano di query
[cq:Page] as [a] /* lucene:cqPageLucene(/oak:index/cqPageLucene?lang=it) jcr:content/cq:tags:my:tag where [a].[jcr:content/cq:tags] = 'my:tag' */
Aggiunta di indexRule per jcr:content/cq:tags
nel cqPageLucene
l’indice consente cq:tags
dati da archiviare in modo ottimizzato.
Quando una query con jcr:content/cq:tags
viene eseguita la restrizione, l’indice può cercare i risultati per valore. Ciò significa che se 100 cq:Page
i nodi hanno myTagNamespace:myTag
in valore, vengono restituiti solo i 100 risultati e gli altri 999.000 sono esclusi dai controlli di restrizione, migliorando le prestazioni di un fattore di 10.000.
Più restrizioni alle query riducono i set di risultati idonei e ottimizzano ulteriormente l’ottimizzazione delle query.
Analogamente, senza una regola di indice aggiuntiva per cq:tags
, anche una query full-text con una restrizione su cq:tags
avrebbe prestazioni insoddisfacenti in quanto i risultati dell’indice restituirebbero tutte le corrispondenze full-text. La restrizione su cq:tags verrebbe filtrata dopo di essa.
Un’altra causa del filtraggio post-indice è rappresentata dagli elenchi di controllo di accesso, che spesso vengono saltati durante lo sviluppo. Verificare che la query non restituisca percorsi che potrebbero essere inaccessibili all'utente. A tal fine, è possibile migliorare la struttura del contenuto e fornire restrizioni rilevanti al percorso della query.
Un modo utile per identificare se l’indice Lucene restituisce molti risultati per restituire un piccolo sottoinsieme come risultato della query è quello di abilitare i registri DEBUG per org.apache.jackrabbit.oak.plugins.index.lucene.LucenePropertyIndex
. In questo modo è possibile visualizzare il numero di documenti caricati dall'indice. Il numero di risultati finali rispetto al numero di documenti caricati non dovrebbe essere sproporzionato. Per ulteriori informazioni, consulta Registrazione.
Monitorare error.log
per le query trasversali:
*WARN* org.apache.jackrabbit.oak.spi.query.Cursors$TraversingCursor Traversed ### nodes ... consider creating an index or changing the query
Visita il AEM Prestazioni query console di gestione e Spiega query lente alla ricerca di piani di query che non risolvono le restrizioni delle proprietà di query per indicizzare le regole di proprietà.
Imposta soglie basse per oak.queryLimitInMemory (ad esempio, 10000) e oak.queryLimitReads (ad esempio, 5000) e ottimizza la query costosa quando si preme un UnsupportedOperationException che indica "La query legge più di x nodi…"
L’impostazione di soglie basse consente di evitare query che richiedono un uso intensivo delle risorse (non supportate da alcun indice o supportate da un indice di copertura inferiore). Ad esempio, una query che legge un milione di nodi produrrebbe un numero elevato di operazioni di I/O e influirebbe negativamente sulle prestazioni complessive dell'applicazione. Pertanto, qualsiasi query che non riesca a causa di limiti superiori deve essere analizzata e ottimizzata.
Monitora i registri per le query che attivano l’attraversamento di nodi di grandi dimensioni o il consumo di memoria heap di grandi dimensioni: "
*WARN* ... java.lang.UnsupportedOperationException: The query read or traversed more than 100000 nodes. To avoid affecting other tasks, processing was stopped.
Monitora i registri per le query che attivano un consumo elevato di memoria heap:
*WARN* ... java.lang.UnsupportedOperationException: The query read more than 500000 nodes in memory. To avoid running out of memory, processing was stopped
Per le versioni da AEM 6.0 a 6.2, è possibile regolare la soglia per l’attraversamento dei nodi tramite i parametri JVM nello script di avvio dell’AEM per evitare che le query di grandi dimensioni sovraccarichi l’ambiente. I valori consigliati sono:
-Doak.queryLimitInMemory=500000
-Doak.queryLimitReads=100000
In AEM 6.3, i due parametri precedenti sono preconfigurati per impostazione predefinita e possono essere modificati tramite OSGi QueryEngineSettings.
Ulteriori informazioni sono disponibili su: https://jackrabbit.apache.org/oak/docs/query/query-engine.html#Slow_Queries_and_Read_Limits
Il motto dell’ottimizzazione delle prestazioni delle query in AEM è:
"Più restrizioni ci sono, meglio è."
Di seguito sono riportati gli adeguamenti consigliati per garantire le prestazioni delle query. Ottimizza innanzitutto la query, un’attività meno invasiva, quindi, se necessario, ottimizza le definizioni dell’indice.
L’AEM supporta i seguenti linguaggi di query:
L’esempio che segue utilizza Query Builder in quanto è il linguaggio di query più comune utilizzato dagli sviluppatori AEM, tuttavia gli stessi principi sono applicabili a JCR-SQL2 e XPath.
Query non ottimizzata
property=jcr:content/contentType
property.value=article-page
Query ottimizzata
type=cq:Page
property=jcr:content/contentType
property.value=article-page
Le query prive di restrizione di tipo di nodo forzano l’AEM ad assumere nt:base
nodetype, che ogni nodo nell'AEM è un sottotipo di, di fatto senza alcuna restrizione del tipo di nodo.
Impostazione type=cq:Page
limita questa query a solo cq:Page
e risolve la query in AEM cqPageLucene, limitando i risultati a un sottoinsieme di nodi cq:Page
nell'AEM.
Query non ottimizzata
type=nt:hierarchyNode
property=jcr:content/contentType
property.value=article-page
Query ottimizzata
type=cq:Page
property=jcr:content/contentType
property.value=article-page
nt:hierarchyNode
è il tipo di nodo principale di cq:Page
. Presupponendo jcr:content/contentType=article-page
è applicato solo a cq:Page
tramite l'applicazione personalizzata di Adobe, questa query restituisce solo cq:Page
nodi in cui jcr:content/contentType=article-page
. Tuttavia, questo flusso rappresenta una restrizione subottimale, perché:
nt:hierarchyNode
(ad esempio, dam:Asset
) aggiungendo inutilmente alla serie di risultati potenziali.nt:hierarchyNode
, tuttavia poiché è presente un indice fornito per cq:Page
.Impostazione type=cq:Page
limita questa query a solo cq:Page
e risolve la query in AEM cqPageLucene, limitando i risultati a un sottoinsieme di nodi (solo nodi cq:Page) nell’AEM.
Query non ottimizzata
property=jcr:content/contentType
property.value=article-page
Query ottimizzata
property=jcr:content/sling:resourceType
property.value=my-site/components/structure/article-page
Modifica della restrizione della proprietà da jcr:content/contentType
(un valore personalizzato) alla proprietà nota sling:resourceType
consente la risoluzione della query nell’indice delle proprietà slingResourceType
che indicizza tutto il contenuto per sling:resourceType
.
Gli indici di proprietà (a differenza degli indici di proprietà Lucene) vengono utilizzati in modo ottimale quando la query non rileva per tipo di nodo e una singola restrizione di proprietà domina il set di risultati.
/content/my-site/us/en
oltre /content/my-site
, o /content/dam
oltre /
.Query non ottimizzata
type=cq:Page
path=/content
property=jcr:content/contentType
property.value=article-page
Query ottimizzata
type=cq:Page
path=/content/my-site/us/en
property=jcr:content/contentType
property.value=article-page
Valutazione della restrizione del percorso da path=/content
a path=/content/my-site/us/en
consente agli indici di ridurre il numero di voci di indice da controllare. Quando la query può limitare bene il percorso, oltre a /content
o /content/dam
, assicurati che l’indice abbia evaluatePathRestrictions=true
.
Nota utilizzo evaluatePathRestrictions
aumenta le dimensioni dell’indice.
LIKE
e fn:XXXX
in base al numero di risultati basati su restrizioni.Query non ottimizzata
type=cq:Page
property=jcr:content/contentType
property.operation=like
property.value=%article%
Query ottimizzata
type=cq:Page
fulltext=article
fulltext.relPath=jcr:content/contentType
La condizione LIKE è lenta da valutare perché non è possibile utilizzare alcun indice se il testo inizia con un carattere jolly ("%…"). La condizione jcr:contains consente l’utilizzo di un indice full-text ed è pertanto da preferirsi. L'indice della proprietà Lucene risolto deve avere indexRule per jcr:content/contentType
con analayzed=true
.
Utilizzo di funzioni di query come fn:lowercase(..)
può essere più difficile da ottimizzare in quanto non esistono equivalenti più veloci (al di fuori di configurazioni più complesse e intrusive dell’analizzatore di indici). È meglio identificare altre restrizioni di ambito per migliorare le prestazioni complessive delle query, richiedendo che le funzioni funzionino sul minor numero possibile di risultati potenziali.
Questa modifica è specifica per Query Builder e non si applica a JCR-SQL2 o XPath.
Utilizzare guessTotal di Query Builder quando l'insieme completo dei risultati è non immediatamente necessari.
Query non ottimizzata
type=cq:Page
path=/content
Query ottimizzata
type=cq:Page
path=/content
p.guessTotal=100
Per i casi in cui l’esecuzione della query è veloce ma il numero di risultati è elevato, p. guessTotal
è un’ottimizzazione critica per le query di Query Builder.
p.guessTotal=100
indica a Query Builder di raccogliere solo i primi 100 risultati. E, per impostare un flag booleano che indica se esiste almeno un altro risultato (ma non quanti altri, poiché contando questo numero si ottiene una lentezza). Questa ottimizzazione è eccezionale per i casi di utilizzo di impaginazione o caricamento infinito, in cui viene visualizzato in modo incrementale solo un sottoinsieme di risultati.
Se la query ottimale viene risolta in un indice delle proprietà, non rimane nulla da fare poiché gli indici delle proprietà sono minimamente ottimizzabili.
In caso contrario, la query deve essere risolta in un indice della proprietà Lucene. Se non è possibile risolvere alcun indice, passare alla Creazione di un nuovo indice.
Se necessario, convertire la query in XPath o JCR-SQL2.
Query di Query Builder
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
XPath generato da query di Query Builder
/jcr:root/content/my-site/us/en//element(*, cq:Page)[jcr:content/@contentType = 'article-page'] order by jcr:content/@publishDate descending
Fornisci XPath (o JCR-SQL2) al generatore di definizioni dell’indice Oak in https://oakutils.appspot.com/generate/index
in modo da poter generare la definizione ottimizzata dell’indice delle proprietà Lucene.
Definizione indice proprietà Lucene generata
- 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"
Unisci manualmente la definizione generata nell’indice della proprietà Lucene esistente in modo additivo. Fai attenzione a non rimuovere le configurazioni esistenti, in quanto potrebbero essere utilizzate per soddisfare altre query.
/oak:index/cqPageLucene
.Verificare che la query non venga risolta in un indice delle proprietà Lucene esistente. In caso affermativo, vedere la sezione precedente sull'ottimizzazione e l'indice esistente.
Se necessario, convertire la query in XPath o JCR-SQL2.
Query di Query Builder
type=myApp:Author
property=firstName
property.value=ira
XPath generato da query di Query Builder
//element(*, myApp:Page)[@firstName = 'ira']
Fornisci XPath (o JCR-SQL2) al generatore di definizioni dell’indice Oak in https://oakutils.appspot.com/generate/index
in modo da poter generare la definizione ottimizzata dell’indice delle proprietà Lucene.
Definizione indice proprietà Lucene generata
- compatVersion = 2
- type = "lucene"
- async = "async"
- jcr:primaryType = oak:QueryIndexDefinition
+ indexRules
+ myApp:AuthorModel
+ properties
+ firstName
- name = "firstName"
- propertyIndex = true
Distribuisci la definizione dell’indice delle proprietà Lucene generato.
Aggiungi la definizione XML fornita da Oak Index Definition Generator per il nuovo indice al progetto AEM che gestisce le definizioni dell’indice Oak (ricorda che tratta le definizioni dell’indice Oak come codice, poiché il codice dipende da esse).
Distribuisci e verifica il nuovo indice seguendo il consueto ciclo di sviluppo del software AEM e verifica che la query si risolva nell’indice e che sia performante.
Al momento della distribuzione iniziale di questo indice, l’AEM lo popola con i dati necessari.
A causa della flessibilità dell’architettura dei contenuti dell’AEM, è difficile prevedere e garantire che gli attraversamenti delle strutture dei contenuti non si evolvano nel tempo fino a raggiungere dimensioni inaccettabili.
Assicurati pertanto che gli indici soddisfino le query, a meno che la combinazione di limitazione del percorso e di limitazione del tipo di nodo garantisca che meno di 20 nodi vengono mai attraversati.
Debugger di Query Builder
CRXDE Lite - Strumento Query
Registrazione di Query Builder
DEBUG @ com.day.cq.search.impl.builder.QueryImpl
Registrazione esecuzione query Oak
DEBUG @ org.apache.jackrabbit.oak.query
Configurazione OSGi delle impostazioni del motore di query Apache Jackrabbit
JMX Mbean di NodeCounter
Generatore definizione indice Oak inhttps://oakutils.appspot.com/generate/index