Risoluzione dei problemi relativi alle query lente

Classificazioni query lente

Sono disponibili 3 classificazioni principali di query lente in AEM, elencate per gravità:

  1. Query senza indice

    • Le query not restituiscono un indice e attraversano il contenuto del JCR per raccogliere i risultati
  2. Query con restrizioni (o con ambito) scadenti

    • Query che si risolvono in un indice, ma devono attraversare tutte le voci di indice per raccogliere i risultati
  3. Query set di risultati di grandi dimensioni

    • Query che restituiscono un numero molto elevato di risultati

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).

Rilevamento delle query senza indice

Durante lo sviluppo

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:

  • PIANO: [nt:unstructured] as [a] /* traverse "/content//*" where ([a].[unindexedProperty] = 'some value') and (isdescendantnode([a], [/content])) */

Post-distribuzione

  • 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
    • Questo messaggio viene registrato solo se non è disponibile alcun indice e se la query attraversa potenzialmente molti nodi. I messaggi non vengono registrati se è disponibile un indice, ma la quantità da attraversare è piccola e quindi veloce.
  • 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.

Rilevamento di query con restrizioni errate

Durante lo sviluppo

Spiega tutte le query e assicurati che vengano risolte in un indice sintonizzato in modo da corrispondere alle restrizioni di proprietà della query.

  • La copertura del piano di query ideale presenta indexRules tutte le restrizioni di proprietà e almeno le restrizioni di proprietà più restrittive nella query.
  • Le query che ordinano i risultati devono essere risolte in un indice di proprietà Lucene con regole di indice per le proprietà ordinate per le quali è impostato orderable=true.

Ad esempio, il valore predefinito 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

    • Non esiste
  • 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.

Post-distribuzione

  • 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.

Rilevamento di query con set di risultati di grandi dimensioni

Durante lo sviluppo

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.

Post-distribuzione

  • 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.
    • Ottimizza la query per ridurre il numero di nodi attraversati
  • 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
    • Ottimizza la query per ridurre il consumo di memoria heap

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

Ottimizzazione delle prestazioni delle query

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.

Regolazione dell’istruzione query

AEM supporta i seguenti linguaggi di query:

  • Query Builder
  • JCR-SQL2
  • XPath

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.

  1. 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.

  1. 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:

    • Un altro nodo eredita da nt:hierarchyNode (ad esempio dam:Asset) aggiungendo inutilmente al set di potenziali risultati.
    • Non esiste un indice fornito AEM per 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.

  1. 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.

  1. 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=/contenta 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.

  1. 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.

  1. 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.

Sintonizzazione indice esistente

  1. 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.

  2. 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.

  3. 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
      
  4. 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"
    
  5. 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.

    1. Individua l'indice della proprietà Lucene esistente che copre cq:Page (utilizzando Index Manager). In questo caso, /oak:index/cqPageLucene.
    2. Identifica il delta di configurazione tra la definizione dell'indice ottimizzato (Passaggio n. 4) e l'indice esistente (/oak:index/cqPageLucene?lang=it) e aggiungi le configurazioni mancanti dall'Indice ottimizzato alla definizione dell'indice esistente.
    3. Per le best practice di reindicizzazione AEM, è in ordine un aggiornamento o un reindicizzazione, in base a se il contenuto esistente verrà effettuato da questa modifica della configurazione dell'indice.

Crea un nuovo indice

  1. 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.

  2. 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']
      
  3. 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
    
  4. 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.

Quando sono OK le query senza indice e traversal?

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.

Strumenti di sviluppo delle query

Adobe supportato

  • Debugger di Query Builder

    • Interfaccia Web per l’esecuzione di query Query Builder e la generazione del supporto XPath (da utilizzare in Explain Query o in Oak Index Definition Generator).
    • AEM all'indirizzo /libs/cq/search/content/querydebug.html
  • CRXDE Lite - Strumento Query

    • Interfaccia Web per l'esecuzione di query XPath e JCR-SQL2.
    • In AEM su /crx/de/index.jsp > Strumenti > Query…
  • Spiega query

    • Dashboard delle operazioni di AEM che fornisce una spiegazione dettagliata (piano query, tempo query e numero di risultati) per qualsiasi query XPATH o JCR-SQL2 specificata.
  • Query lente/popolari

    • Una dashboard delle operazioni di AEM elenca le query lente e popolari recenti eseguite su AEM.
  • Gestione indice

    • Un'interfaccia Web AEM Operations che visualizza gli indici sull'istanza AEM; facilita la comprensione degli indici già esistenti, che possono essere targetizzati o potenziati.
  • Registrazione

    • 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

Community supportata

In questa pagina

Adobe Maker Awards Banner

Time to shine!

Apply now for the 2021 Adobe Experience Maker Awards.

Apply now
Adobe Maker Awards Banner

Time to shine!

Apply now for the 2021 Adobe Experience Maker Awards.

Apply now