Sono disponibili 3 classificazioni principali di query lente in AEM, elencate per gravità:
Query senza indice
Query con restrizioni (o con ambito) scadenti
Query set di risultati di grandi dimensioni
Le prime 2 classificazioni di query (senza indice e con restrizioni limitate) sono lente, perché forzano il motore di query Oak a ispezionare ogni risultato potenziale (nodo di contenuto o voce di indice) per identificare quale appartiene al set di risultati effettivo.
L'atto di ispezionare ogni potenziale risultato è quello che viene chiamato Traversing.
Poiché ogni risultato potenziale deve essere esaminato, il costo per determinare il set di risultati effettivo cresce in modo lineare con il numero di risultati potenziali.
L’aggiunta di restrizioni alle query e indici di ottimizzazione consente di memorizzare i dati di indice in un formato ottimizzato che consenta 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 genera un'eccezione. Questo limite non esiste per impostazione predefinita nelle versioni AEM precedenti a AEM 6.3, ma può essere impostato tramite la configurazione OSGi delle impostazioni del motore di query Apache Jackrabbit e il fagiolo JMX QueryEngineSettings (proprietà LimitReads).
Spiegare le query all e assicurarsi che i relativi piani di query non contengano /* traverse spiegazione al loro interno. Esempio di attraversamento del piano di query:
[nt:unstructured] as [a] /* traverse "/content//*" where ([a].[unindexedProperty] = 'some value') and (isdescendantnode([a], [/content])) */
Monitora error.log
per le query traversal senza indice:
*INFO* org.apache.jackrabbit.oak.query.QueryImpl Traversal query (query without index) ... ; consider creating and index
Visita la console delle operazioni AEM Prestazioni query e Spiega query lente in cerca di spiegazioni di query trasversali o senza spiegazioni di query dell'indice.
Spiega tutte le query e assicurati che vengano risolte in un indice sintonizzato in modo da corrispondere alle restrizioni di proprietà della query.
indexRules
tutte le restrizioni di proprietà e almeno le restrizioni di proprietà più restrittive 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 Builder
type=cq:Page
property=jcr:content/cq:tags
property.value=my:tag
Piano query
[cq:Page] as [a] /* lucene:cqPageLucene(/oak:index/cqPageLucene?lang=it) *:* where [a].[jcr:content/cq:tags] = 'my:tag' */
Questa query viene risolta nell'indice cqPageLucene
, ma poiché non esiste alcuna regola di indice di proprietà per jcr:content
o cq:tags
, quando si valuta questa restrizione, ogni record nell'indice cqPageLucene
viene controllato per determinare una corrispondenza. Ciò significa che se l'indice contiene 1 milione di nodi cq:Page
, vengono controllati 1 milione di record per determinare il set di risultati.
Dopo aver aggiunto la 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 Builder
type=cq:Page
property=jcr:content/cq:tags
property.value=myTagNamespace:myTag
Piano 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' */
L'aggiunta di indexRule per jcr:content/cq:tags
nell'indice cqPageLucene
consente di memorizzare i dati cq:tags
in modo ottimizzato.
Quando viene eseguita una query con la restrizione jcr:content/cq:tags
, l'indice può cercare i risultati per valore. Ciò significa che se 100 cq:Page
nodi hanno myTagNamespace:myTag
come 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.
Naturalmente, ulteriori restrizioni alle query riducono i set di risultati idonei e ottimizzano ulteriormente l’ottimizzazione delle query.
Allo stesso modo, senza una regola di indice aggiuntiva per la proprietà cq:tags
, anche una query di testo completo con una restrizione su cq:tags
avrebbe prestazioni sbagliate in quanto i risultati dell'indice restituirebbero tutte le corrispondenze di testo completo. La restrizione su cq:tags verrebbe filtrata dopo di essa.
Un'altra causa del filtraggio post-indice è l'accesso agli elenchi di controllo che spesso viene mancato durante lo sviluppo. Assicurati che la query non restituisca percorsi che potrebbero essere inaccessibili all’utente. Questo di solito può essere fatto da una migliore struttura del contenuto, oltre a fornire pertinenti restrizioni del percorso sulla query.
Un modo utile per identificare se l'indice Lucene sta restituendo molti risultati per restituire un sottoinsieme molto piccolo come risultato della query è quello di abilitare i registri DEBUG per org.apache.jackrabbit.oak.plugins.index.lucene.LucenePropertyIndex
e vedere quanti documenti vengono caricati dall'indice. Il numero di risultati finali rispetto al numero di documenti caricati non dovrebbe essere sproporzionato. Per ulteriori informazioni, consulta Registrazione.
Monitorare le error.log
query traversal:
*WARN* org.apache.jackrabbit.oak.spi.query.Cursors$TraversingCursor Traversed ### nodes ... consider creating an index or changing the query
Visita la console delle operazioni AEM Prestazioni query e Spiega query lente alla ricerca di piani di query che non risolvono le restrizioni delle proprietà query alle regole delle proprietà di indice.
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 dicendo "La query legge più di x nodi…"
Questo consente di evitare query ad alta intensità di risorse (ad es. non supportato da alcun indice o sostenuto da un indice di copertura inferiore). Ad esempio, una query che legge nodi 1M porterebbe a un sacco di IO e avrebbe un impatto negativo sulle prestazioni complessive dell'applicazione. Quindi qualsiasi query che non riesce a causa dei limiti sopra indicati deve essere analizzata e ottimizzata.
Monitora i log per le query che attivano il consumo di memoria heap di grandi nodi o traversal 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 grande consumo 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 AEM 6.0 - 6.2, è possibile ottimizzare la soglia per l'attraversamento dei nodi tramite i parametri JVM nello script di avvio AEM per evitare che le query di grandi dimensioni sovraccarichino l'ambiente. I valori consigliati sono :
-Doak.queryLimitInMemory=500000
-Doak.queryLimitReads=100000
In AEM 6.3, i due parametri di cui sopra sono preconfigurati per impostazione predefinita e possono essere modificati tramite OSGi QueryEngineSettings.
Ulteriori informazioni disponibili in : 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, meglio è."
Di seguito sono riportati gli adeguamenti consigliati per garantire le prestazioni della query. Prima regola la query, un'attività meno invasiva e poi, se necessario, regola le definizioni degli indici.
AEM supporta i seguenti linguaggi di query:
Nell'esempio seguente viene utilizzato 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.
Aggiungi una restrizione del tipo di nodo in modo che la query venga risolta in un indice della proprietà Lucene esistente.
Query non ottimizzata
property=jcr:content/contentType
property.value=article-page
Query ottimizzata
type=cq:Page
property=jcr:content/contentType
property.value=article-page
Query prive di una forza di restrizione del tipo di nodo AEM per assumere il tipo di nodo nt:base
, di cui ogni nodo in AEM è un sottotipo, con conseguente assenza di restrizioni del tipo di nodo.
L'impostazione type=cq:Page
limita questa query solo ai nodi cq:Page
e risolve la query in AEM cqPageLucene, limitando i risultati a un sottoinsieme di nodi (solo cq:Page
nodi) in AEM.
Regola la restrizione del tipo di nodo della query in modo che la query venga risolta in un indice di proprietà Lucene esistente.
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
, e supponendo jcr:content/contentType=article-page
sia applicato solo ai cq:Page
nodi tramite la nostra applicazione personalizzata, questa query restituirà solo cq:Page
nodi in cui jcr:content/contentType=article-page
. Si tratta però di una restrizione non ottimale, in quanto:
nt:hierarchyNode
(ad esempio dam:Asset
) aggiungendo inutilmente al set di potenziali risultati.nt:hierarchyNode
, tuttavia esiste un indice fornito per cq:Page
.L'impostazione type=cq:Page
limita questa query solo ai nodi cq:Page
e risolve la query in AEM cqPageLucene, limitando i risultati a un sottoinsieme di nodi (solo nodi cq:Page) in AEM.
In alternativa, regola le limitazioni della proprietà in modo che la query venga risolta in un indice proprietà esistente.
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
La modifica della restrizione della proprietà da jcr:content/contentType
(un valore personalizzato) alla proprietà nota sling:resourceType
consente alla query di risolvere l'indice della proprietà slingResourceType
che indicizza tutto il contenuto per sling:resourceType
.
Gli indici delle proprietà (a differenza degli indici delle proprietà Lucene) vengono utilizzati al meglio quando la query non viene rilevata per tipo di nodo e una singola restrizione delle proprietà domina il set di risultati.
Aggiungi alla query la restrizione più restrittiva possibile al percorso. Ad esempio, preferisci /content/my-site/us/en
rispetto a /content/my-site
o /content/dam
rispetto a /
.
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
L’ambito 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 che devono essere ispezionate. Quando la query può limitare il percorso molto bene, oltre solo /content
o /content/dam
, assicurati che l'indice sia evaluatePathRestrictions=true
.
Nota che utilizzando evaluatePathRestrictions
aumenta la dimensione dell'indice.
Se possibile, evita funzioni/operazioni di query quali: LIKE
e fn:XXXX
come costi variano 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 di utilizzare un indice fulltext ed è pertanto preferita. Questo richiede che l'indice di proprietà Lucene risolto abbia indexRule per jcr:content/contentType
con analayzed=true
.
L’utilizzo di funzioni di query come fn:lowercase(..)
può essere più difficile da ottimizzare in quanto non ci sono equivalenti più veloci (al di fuori di configurazioni più complesse e intrusive dell’analizzatore dell’indice). È consigliabile identificare altre restrizioni dell'ambito per migliorare le prestazioni complessive della query, richiedendo alle funzioni di funzionare sul set più piccolo possibile di risultati potenziali.
Questa regolazione è specifica di Query Builder e non si applica a JCR-SQL2 o XPath.
Utilizza Query Builder' guessTotal quando l'intero set di risultati è non immediatamente necessario.
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
comunica a Query Builder di raccogliere solo i primi 100 risultati e imposta un flag booleano che indica se esistono almeno uno o più risultati (ma non quanti, in quanto contando questo numero si otterrebbe una lentezza). Questa ottimizzazione eccelle per l’impaginazione o i casi d’uso di caricamento infinito, in cui viene visualizzato solo un sottoinsieme di risultati in modo incrementale.
Se la query ottimale viene risolta in un indice di proprietà, allora non c'è più niente da fare perché gli indici di proprietà sono minimamente sintonizzabili.
In caso contrario, la query deve essere risolta in un indice di proprietà Lucene. Se non è possibile risolvere alcun indice, passa a Creazione di un nuovo indice.
Se necessario, convertire la query in XPath o JCR-SQL2.
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 dalla query Query Builder
/jcr:root/content/my-site/us/en//element(*, cq:Page)[jcr:content/@contentType = 'article-page'] order by jcr:content/@publishDate descending
Fornisci l'XPath (o JCR-SQL2) a Generatore di definizione dell'indice Oak per generare la definizione ottimizzata dell'indice di proprietà Lucene.
Definizione dell'indice della proprietà Lucene generato
- 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 proprietà Lucene esistente in modo additivo. Presta attenzione a non rimuovere le configurazioni esistenti in quanto possono essere utilizzate per soddisfare altre query.
/oak:index/cqPageLucene
.Verifica che la query non venga risolta in un indice di 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 Builder
type=myApp:Author
property=firstName
property.value=ira
XPath generato dalla query Query Builder
//element(*, myApp:Page)[@firstName = 'ira']
Fornisci l'XPath (o JCR-SQL2) a Generatore di definizione dell'indice Oak per generare la definizione ottimizzata dell'indice di proprietà Lucene.
Definizione dell'indice della proprietà Lucene generato
- compatVersion = 2
- type = "lucene"
- async = "async"
- jcr:primaryType = oak:QueryIndexDefinition
+ indexRules
+ myApp:AuthorModel
+ properties
+ firstName
- name = "firstName"
- propertyIndex = true
Distribuisci la definizione dell'indice di proprietà Lucene generato.
Aggiungi la definizione XML fornita da Oak Index Definition Generator per il nuovo indice al progetto AEM che gestisce le definizioni degli indici Oak (ricorda, tratta le definizioni degli indici Oak come codice, dal momento che il codice dipende da loro).
Distribuire e testare il nuovo indice seguendo il consueto ciclo di vita di sviluppo del software AEM e verificare che la query sia risolta nell'indice e che la query sia performante.
Al momento della distribuzione iniziale di questo indice, AEM popolerà l'indice con i dati richiesti.
A causa AEM’architettura dei contenuti flessibile, è difficile prevedere e garantire che l’attraversamento delle strutture dei contenuti non evolva nel tempo per diventare eccessivamente grande.
Assicurati pertanto che gli indici soddisfino le query, a meno che la combinazione di restrizione del percorso e restrizione del tipo di nodo garantisca che meno di 20 nodi siano mai attraversati.
Debugger di Query Builder
CRXDE Lite - Strumento Query
Registrazione 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
Mbean JMX NodeCounter
Generatore di definizione dell'indice Oak