Show Menu
ARGOMENTI×

Risoluzione dei problemi relativi a query lente

Classificazioni query lente

In AEM sono disponibili 3 principali classificazioni di query lente, elencate per gravità:
  1. Query senza indice
    • Query che non si risolvono in un indice e attraversano il contenuto del JCR per raccogliere i risultati
  2. Query limitate (o con ambito) scadenti
    • Query che si risolvono in un indice, ma che devono scorrere 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'ispezione di ogni potenziale risultato è ciò che viene chiamato Traversing.
Poiché ogni potenziale risultato deve essere analizzato, il costo per determinare il set di risultati effettivo cresce in modo lineare con il numero di potenziali risultati.
L'aggiunta di restrizioni di 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 controllo lineare dei set di risultati potenziali.
In AEM 6.3, per impostazione predefinita, quando viene raggiunto un attraversamento di 100.000, la query ha esito negativo e genera un’eccezione. Questo limite non esiste per impostazione predefinita nelle versioni di AEM precedenti alla 6.3, ma può essere impostato tramite la configurazione OSGi delle impostazioni del motore di query Apache Jackrabbit e il fagiolo JMX QueryEngineSettings (Limite proprietà).

Rilevamento di query senza indice

Durante lo sviluppo

Spiegare tutte le query e assicurarsi che i relativi piani di query non contengano il/* traversa la spiegazione. Esempio di piano query di navigazione:
  • PIANO: [nt:unstructured] as [a] /* traverse "/content//*" where ([a].[unindexedProperty] = 'some value') and (isdescendantnode([a], [/content])) */

Post-distribuzione

  • Monitorate le query error.log trasversali 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 scorrere è piccola e quindi veloce.
  • Visitate la console delle operazioni di AEM Query Performance e spiegate le query lente alla ricerca di spiegazioni sulle query trasversali o prive di spiegazioni sulle query di indice.

Rilevamento Di Query Limitate

Durante lo sviluppo

Spiegare tutte le query e assicurarsi che vengano risolte in un indice sintonizzato in modo che corrispondano alle restrizioni di proprietà della query.
  • La copertura del piano query ideale include tutte le restrizioni indexRules di proprietà e almeno le restrizioni di proprietà più restrittive nella query.
  • Le query che ordinano i risultati devono essere risolte in un indice delle proprietà Lucene con regole di indice per le proprietà ordinate per quelle impostate 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 di indice
    • Non esiste fuori dalla casella
  • 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) *:* where [a].[jcr:content/cq:tags] = 'my:tag' */
Questa query corrisponde all' cqPageLucene indice, ma poiché non esiste alcuna regola di indice delle proprietà per jcr:content o cq:tags , quando viene valutata questa restrizione, ogni record nell' cqPageLucene indice viene controllato per determinare una corrispondenza. Ciò significa che se l'indice contiene 1 milione di cq:Page nodi, vengono controllati 1 milione di record per determinare il set di risultati.
Dopo l'aggiunta della regola di indice cq:tags
  • cq:tags, regola di 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) jcr:content/cq:tags:my:tag where [a].[jcr:content/cq:tags] = 'my:tag' */
L'aggiunta di indexRule per jcr:content/cq:tags nell' cqPageLucene indice consente di memorizzare cq:tags i dati in modo ottimizzato.
Quando viene eseguita una query con la jcr:content/cq:tags restrizione, l'indice può cercare i risultati per valore. Ciò significa che se 100 cq:Page nodi hanno myTagNamespace:myTag come valore, vengono restituiti solo quei 100 risultati, e gli altri 999.000 sono esclusi dai controlli di restrizione, migliorando le prestazioni di un fattore di 10.000.
Naturalmente, ulteriori limitazioni delle 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 cq:tags proprietà, anche una query full-text con una restrizione su cq:tags non avrebbe funzionato correttamente, in quanto i risultati dell'indice restituirebbero tutte le corrispondenze full-text. La restrizione relativa ai tag cq:tags viene filtrata dopo di essa.
Un'altra causa del filtraggio post-indice è l'Access Control Lists, che spesso viene persa durante lo sviluppo. Assicurarsi che la query non restituisca percorsi che potrebbero essere inaccessibili all'utente. Questa operazione può essere eseguita in genere mediante una migliore struttura del contenuto e l'indicazione di limitazioni rilevanti del percorso della query.
Un modo utile per identificare se l'indice di 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. For more information, see Logging .

Post-distribuzione

  • Monitorare le query error.log di attraversamento:
    • *WARN* org.apache.jackrabbit.oak.spi.query.Cursors$TraversingCursor Traversed ### nodes ... consider creating an index or changing the query
  • Visitate la console delle operazioni di AEM Query Performance e spiegate le 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

Impostare soglie basse per oak.queryLimitInMemory (ad esempio 10000) e oak.queryLimitReads (ad esempio 5000) e ottimizzare la query costosa quando si tocca un UnsupportedOperationException che dice "La query ha letto più di x nodi..."
In questo modo si evitano le query che richiedono molte risorse (ad esempio non sostenuto da alcun indice o sostenuto da un indice di copertura inferiore). Ad esempio, una query che legge nodi 1M porterebbe a molti IO e avrebbe un impatto negativo sulle prestazioni complessive dell'applicazione. Pertanto, qualsiasi query che non riesce a causa di limiti superiori deve essere analizzata e ottimizzata.

Post-distribuzione

  • Monitorare i registri per le query che attivano il consumo di memoria heap di grandi nodi: "
    • *WARN* ... java.lang.UnsupportedOperationException: The query read or traversed more than 100000 nodes. To avoid affecting other tasks, processing was stopped.
    • Ottimizzare la query per ridurre il numero di nodi attraversati
  • Monitorare 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
    • Ottimizzare la query per ridurre il consumo di memoria heap
Per le versioni di AEM 6.0 - 6.2, puoi regolare la soglia per l’attraversamento dei nodi tramite i parametri JVM nello script di avvio di AEM per evitare che query di grandi dimensioni sovraccarichino l’ambiente. I valori consigliati sono:
  • -Doak.queryLimitInMemory=500000
  • -Doak.queryLimitReads=100000
In AEM 6.3, i due parametri indicati sopra sono preconfigurati per impostazione predefinita e possono essere modificati tramite OSGi QueryEngineSettings.

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. Innanzitutto, sintonizzate la query, un'attività meno intrusiva e, se necessario, regolate le definizioni di indice.

Adeguamento dell'istruzione Query

AEM supporta le seguenti lingue di query:
  • Query Builder
  • JCR-SQL2
  • XPath
Nell'esempio seguente viene utilizzato Query Builder, il linguaggio di query più comune utilizzato dagli sviluppatori AEM, tuttavia gli stessi principi sono applicabili a JCR-SQL2 e XPath.
  1. Aggiungete una restrizione relativa al tipo di nodo in modo che la query corrisponda a un indice esistente della proprietà Lucene.
    • 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 restrizioni relative ai tipi di nodi forzano AEM ad assumere il nt:base tipo di nodo, di cui ogni nodo in AEM è un sottotipo, e di conseguenza non determinano restrizioni relative ai tipi di nodi.
    L'impostazione type=cq:Page limita questa query ai soli cq:Page nodi e risolve la query a cqPageLucene di AEM, limitando i risultati a un sottoinsieme di nodi (solo cq:Page nodi) in AEM.
  2. Regolare la limitazione del tipo di nodo della query in modo che la query corrisponda a un indice esistente della proprietà Lucene.
    • 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 padre 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 dove jcr:content/contentType=article-page . Questa è una restrizione non ottimale, però, perché:
    • Un altro nodo eredita da nt:hierarchyNode (ad esempio dam:Asset ) aggiunta non necessaria al set di risultati potenziali.
    • Nessun indice fornito da AEM esiste nt:hierarchyNode , tuttavia, in quanto è presente un indice fornito per cq:Page . L’impostazione type=cq:Page limita questa query ai soli cq:Page nodi e risolve la query a cqPageLucene di AEM, limitando i risultati a un sottoinsieme di nodi (solo nodi cq:Page) in AEM.
  3. In alternativa, regolate le restrizioni delle proprietà in modo che la query corrisponda a un indice delle 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
               ```
      
      
    Modificando la restrizione delle proprietà da jcr:content/contentType (un valore personalizzato) a una proprietà nota, la query sling:resourceType consente di risolvere l'indice delle 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 visualizzata per tipo di nodo e una singola limitazione di proprietà domina il set di risultati.
  4. Aggiungete alla query il limite più restrittivo possibile per il percorso. Ad esempio, preferisci /content/my-site/us/en over /content/my-site , o /content/dam over / .
    • 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 limitazione del percorso path=/content per path=/content/my-site/us/en consentire agli indici di ridurre il numero di voci indice da ispezionare. Quando la query può limitare il percorso molto bene, oltre solo /content o /content/dam , assicurarsi che l'indice sia evaluatePathRestrictions=true .
    Nota: l'utilizzo evaluatePathRestrictions aumenta la dimensione dell'indice.
  5. Quando possibile, evitare funzioni/operazioni di query quali: LIKE e fn:XXXX come i loro costi si adattano al numero di risultati basati sulle 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'uso di un indice full-text, ed è pertanto preferita. Ciò richiede che l'indice delle proprietà Lucene risolto disponga di 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 esistono equivalenti più veloci (al di fuori di configurazioni di analizzatore indice più complesse e intrusive). È consigliabile identificare altre limitazioni di ambito per migliorare le prestazioni complessive della query, richiedendo che le funzioni operino sulla più piccola serie di risultati potenziali possibili.
  6. Questa regolazione è specifica di Query Builder e non si applica a JCR-SQL2 o XPath.
    Utilizzate Query Builder'Total quando l'intero set di risultati è **not **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 delle query è rapida 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 un altro risultato (ma non quanti altri risultati, in quanto contando questo numero si verificherebbe un rallentamento). Questa ottimizzazione è eccezionale per l’impaginazione o per infiniti casi di utilizzo del caricamento, in cui solo un sottoinsieme di risultati viene visualizzato in modo incrementale.

Ottimizzazione indice esistente

  1. Se la query ottimale viene risolta in un Indice proprietà, non rimane nulla da fare in quanto gli indici delle proprietà sono minimamente sintonizzabili.
  2. In caso contrario, la query deve essere risolta in un indice delle proprietà Lucene. Se non è possibile risolvere alcun indice, passare 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. Fornire l'XPath (o JCR-SQL2) al generatore di definizione dell'indice Oak per generare la definizione ottimizzata dell'indice di 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"
    
    
  5. Unisci manualmente la definizione generata nell'indice esistente delle proprietà Lucene in modo additivo. Prestate attenzione a non rimuovere le configurazioni esistenti in quanto potrebbero essere utilizzate per soddisfare altre query.
    1. Individuate l'indice delle proprietà Lucene esistente che copre cq:Page (utilizzando la funzione di gestione dell'indice). In questo caso, /oak:index/cqPageLucene .
    2. Identificare il delta di configurazione tra la definizione dell'indice ottimizzata (Passaggio 4) e l'indice esistente (/oak:index/cqPageLucene), quindi aggiungere le configurazioni mancanti dall'indice ottimizzato alla definizione dell'indice esistente.
    3. In base alle best practice di reindicizzazione di AEM, è possibile aggiornare o reindicizzare in ordine, in base al fatto che il contenuto esistente verrà influenzato da questa modifica alla configurazione dell'indice.

Crea nuovo indice

  1. Verificare che la query non venga risolta in un indice delle proprietà Lucene esistente. In caso contrario, 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. Fornire l'XPath (o JCR-SQL2) al generatore di definizione dell'indice Oak per generare la definizione ottimizzata dell'indice di 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
    
    
  4. Distribuisci la definizione dell'indice delle proprietà Lucene generato.
    Aggiungete la definizione XML fornita da Oak Index Definition Generator per il nuovo indice al progetto AEM che gestisce le definizioni dell'indice Oak (ricordate, considera le definizioni dell'indice Oak come codice, dal momento che il codice dipende da esse).
    Distribuisci e verifica il nuovo indice in base al normale ciclo di vita del software AEM e verifica che la query sia risolta nell’indice e che la query sia performante.
    Dopo la distribuzione iniziale di questo indice, AEM inserirà l’indice con i dati richiesti.

Quando sono OK le query senza indice e traversal?

A causa dell'architettura flessibile dei contenuti di AEM, è difficile prevedere e garantire che l'evoluzione delle strutture dei contenuti non si verifichi nel tempo, per essere inaccettabilmente ampia.
Pertanto, assicurarsi che gli indici soddisfino le query, a meno che la combinazione di restrizione percorso e restrizione nodetype garantisca che meno di 20 nodi vengano mai attraversati.

Strumenti di sviluppo query

Adobe Supportato

  • Debugger Query Builder
    • Interfaccia Web per l'esecuzione di query Query Builder e per generare l'XPath di supporto (da utilizzare in Generatore di definizione di query o indice Oak).
  • CRXDE Lite - Strumento Query
    • Interfaccia Web per l'esecuzione di query XPath e JCR-SQL2.
    • In AEM, all’indirizzo /crx/de/index.jsp > Strumenti > Query...
    • Dashboard delle operazioni AEM che fornisce una spiegazione dettagliata (piano query, tempo query e numero di risultati) per qualsiasi query XPATH o JCR-SQL2 specificata.
    • Una dashboard delle operazioni AEM che elenca le recenti query lente e popolari eseguite su AEM.
    • Un’interfaccia Web AEM Operations che visualizza gli indici sull’istanza AEM; facilita la comprensione degli indici già esistenti, che possono essere mirati o incrementati.
    • 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
  • NodeCounter JMX

Community supportata

    • Generare un indice di proprietà di Lucence ottimale dalle istruzioni di query XPath o JCR-SQL2.
    • Estensione del browser Web Google Chrome che espone i dati di registro per ogni richiesta, incluse le query eseguite e i relativi piani di query, nella console degli strumenti di sviluppo del browser.
    • Richiede l'installazione e l'abilitazione di Sling Log Tracer 1.0.2+ su AEM.