Há 3 classificações principais de query lentos em AEM, listadas por gravidade:
Query sem índice
Query com pouca restrição (ou com escopo)
Grandes query de conjunto de resultados
As primeiras 2 classificações de query (sem índice e com pouca restrição) são lentas, pois forçam o mecanismo de query Oak a inspecionar cada resultado potencial (nó de conteúdo ou entrada de índice) para identificar qual pertence ao conjunto de resultados real.
O ato de inspecionar cada resultado potencial é o que se chama de Navegação.
Uma vez que cada resultado potencial deve ser inspecionado, o custo para determinar o conjunto de resultados real cresce linearmente com o número de resultados potenciais.
A adição de restrições de query e índices de ajuste permite que os dados de índice sejam armazenados em um formato otimizado, permitindo uma recuperação rápida de resultados e reduz ou elimina a necessidade de inspeção linear de conjuntos de resultados potenciais.
No AEM 6.3, por padrão, quando uma passagem de 100.000 é atingida, o query falha e lança uma exceção. Esse limite não existe por padrão em versões AEM anteriores à AEM 6.3, mas pode ser definido por meio das Configurações do mecanismo de Query Apache Jackrabbit Configuração OSGi e do bean QueryEngineSettings JMX (propriedade LimitReads).
Explique todos os query e certifique-se de que seus planos de query não contêm o /* explicação traverse neles. Exemplo de plano de query de percurso:
[nt:unstructured] as [a] /* traverse "/content//*?lang=pt-BR" where ([a].[unindexedProperty] = 'some value') and (isdescendantnode([a], [/content])) */
Monitore error.log
para query transversais sem índice:
*INFO* org.apache.jackrabbit.oak.query.QueryImpl Traversal query (query without index) ... ; consider creating and index
Visite o console de operações AEM Desempenho do Query e Explique query lentos que procuram explicações transversais ou de query de índice.
Explique todos os query e verifique se eles foram resolvidos para um índice ajustado para corresponder às restrições de propriedade do query.
indexRules
para todas as restrições de propriedade e, no mínimo, para as restrições de propriedade mais restritas no query.orderable=true.
cqPageLucene
não tem uma regra de índice para jcr:content/cq:tags
Antes de adicionar a regra de índice cq:tags
cq:regras de índice de tags
Query Construtor de query
type=cq:Page
property=jcr:content/cq:tags
property.value=my:tag
plano de query
[cq:Page] as [a] /* lucene:cqPageLucene(/oak:index/cqPageLucene?lang=pt-BR) *:* where [a].[jcr:content/cq:tags] = 'my:tag' */
Este query resolve o índice cqPageLucene
, mas como não existe uma regra de índice de propriedade para jcr:content
ou cq:tags
, quando esta restrição é avaliada, todos os registros no índice cqPageLucene
são verificados para determinar uma correspondência. Isso significa que, se o índice contiver 1 milhão de nós cq:Page
, 1 milhão de registros serão verificados para determinar o conjunto de resultados.
Após adicionar a regra de índice cq:tags
cq:regras de índice de tags
/oak:index/cqPageLucene/indexRules/cq:Page/properties/cqTags
@name=jcr:content/cq:tags
@propertyIndex=true
Query Construtor de query
type=cq:Page
property=jcr:content/cq:tags
property.value=myTagNamespace:myTag
plano de query
[cq:Page] as [a] /* lucene:cqPageLucene(/oak:index/cqPageLucene?lang=pt-BR) jcr:content/cq:tags:my:tag where [a].[jcr:content/cq:tags] = 'my:tag' */
A adição de indexRule para jcr:content/cq:tags
no índice cqPageLucene
permite que os dados cq:tags
sejam armazenados de forma otimizada.
Quando um query com a restrição jcr:content/cq:tags
é executado, o índice pode procurar os resultados por valor. Isso significa que se 100 cq:Page
nós tiverem myTagNamespace:myTag
como valor, apenas esses 100 resultados serão retornados e os outros 999.000 serão excluídos das verificações de restrições, melhorando o desempenho em um fator de 10.000.
É claro que novas restrições ao query reduzem os conjuntos de resultados elegíveis e otimizam ainda mais a otimização do query.
Da mesma forma, sem uma regra de índice adicional para a propriedade cq:tags
, mesmo um query de texto completo com uma restrição em cq:tags
teria um desempenho ruim, pois os resultados do índice retornariam todas as correspondências de texto completo. A restrição nas tags cq:seria filtrada depois.
Outra causa da filtragem pós-índice são Listas Controles de acesso que muitas vezes se perdem durante o desenvolvimento. Tente se certificar de que o query não retorne caminhos que possam estar inacessíveis ao usuário. Isso geralmente pode ser feito por uma estrutura de conteúdo melhor, além de fornecer a restrição de caminho relevante no query.
Uma maneira útil de identificar se o índice Lucene está retornando muitos resultados para retornar um subconjunto muito pequeno como resultado do query é habilitar registros DEBUG para org.apache.jackrabbit.oak.plugins.index.lucene.LucenePropertyIndex
e ver quantos documentos estão sendo carregados do índice. O número de eventuais resultados versus o número de documentos carregados não deve ser desproporcionado. Para obter mais informações, consulte Registro.
Monitore error.log
para query transversais:
*WARN* org.apache.jackrabbit.oak.spi.query.Cursors$TraversingCursor Traversed ### nodes ... consider creating an index or changing the query
Visite o console de operações AEM Desempenho do Query e Explique query lentos que procuram planos de query que não resolvam as restrições de propriedade do query para indexar as regras de propriedade.
Defina limites baixos para oak.queryLimitInMemory (por exemplo, 10000) e oak.queryLimitReads (por exemplo, 5000) e otimize o query caro ao clicar em UnsupportedOperationException dizendo "O query lê mais de x nós…".
Isso ajuda a evitar query que consomem muitos recursos (ou seja, não suportado por qualquer índice ou apoiado por um índice de cobertura menor). Por exemplo, um query que lê nós 1M levaria a uma grande quantidade de E/S e afetaria negativamente o desempenho geral do aplicativo. Assim, qualquer query que falhe devido aos limites acima devem ser analisados e otimizados.
Monitore os registros em busca de query que acionem um grande nó transversal ou grande consumo de memória heap:
*WARN* ... java.lang.UnsupportedOperationException: The query read or traversed more than 100000 nodes. To avoid affecting other tasks, processing was stopped.
Monitore os registros em busca de query que acionem um grande consumo de memória heap:
*WARN* ... java.lang.UnsupportedOperationException: The query read more than 500000 nodes in memory. To avoid running out of memory, processing was stopped
Para as versões AEM 6.0 - 6.2, é possível ajustar o limite para a passagem de nó pelos parâmetros JVM no script de start AEM para impedir que query grandes sobrecarreguem o ambiente. Os valores recomendados são:
-Doak.queryLimitInMemory=500000
-Doak.queryLimitReads=100000
No AEM 6.3, os 2 parâmetros acima são pré-configurados por padrão e podem ser modificados por meio do OSGi QueryEngineSettings.
Mais informações disponíveis em: https://jackrabbit.apache.org/oak/docs/query/query-engine.html#Slow_Queries_and_Read_Limits
O lema da otimização do desempenho do query no AEM é:
"Quanto mais restrições, melhor."
A seguir, são apresentados os ajustes recomendados para garantir o desempenho do query. Primeiro ajuste o query, uma atividade menos intrusiva e, se necessário, ajuste as definições de índice.
AEM suporta os seguintes idiomas de query:
O exemplo a seguir usa o Construtor de Query como a linguagem de query mais comum usada pelos desenvolvedores AEM, no entanto, os mesmos princípios se aplicam ao JCR-SQL2 e XPath.
Adicione uma restrição de tipo de nó para que o query seja resolvido em um Índice de propriedades do Lucene existente.
Query não otimizado
property=jcr:content/contentType
property.value=article-page
Query otimizado
type=cq:Page
property=jcr:content/contentType
property.value=article-page
Query sem uma restrição de tipo nodetype AEM assumir o tipo de nó nt:base
, do qual cada nó no AEM é um subtipo de, resultando efetivamente em nenhuma restrição de tipo de nó.
A configuração type=cq:Page
restringe esse query a apenas nós cq:Page
e resolve o query para AEM cqPageLucene, limitando os resultados a um subconjunto de nós (somente nós cq:Page
) no AEM.
Ajuste a restrição de tipo de query para que o query seja resolvido para um Índice de propriedades Lucene existente.
Query não otimizado
type=nt:hierarchyNode
property=jcr:content/contentType
property.value=article-page
Query otimizado
type=cq:Page
property=jcr:content/contentType
property.value=article-page
nt:hierarchyNode
é o tipo de nó pai de cq:Page
e, supondo que jcr:content/contentType=article-page
seja aplicado somente a cq:Page
nós por meio de nosso aplicativo personalizado, esse query retornará apenas cq:Page
nós onde jcr:content/contentType=article-page
. No entanto, esta é uma restrição subótima, porque:
nt:hierarchyNode
(por exemplo, dam:Asset
) adicionando desnecessariamente ao conjunto de resultados potenciais.nt:hierarchyNode
, no entanto, como existe um índice fornecido para cq:Page
.A configuração type=cq:Page
restringe esse query a apenas nós cq:Page
e resolve o query para AEM cqPageLucene, limitando os resultados a um subconjunto de nós (somente nós cq:Page) no AEM.
Ou ajuste as restrições de propriedade para que o query seja resolvido como um Índice de propriedades existente.
Query não otimizado
property=jcr:content/contentType
property.value=article-page
Query otimizado
property=jcr:content/sling:resourceType
property.value=my-site/components/structure/article-page
Alterar a restrição de propriedade de jcr:content/contentType
(um valor personalizado) para a propriedade conhecida sling:resourceType
permite que o query resolva para o índice de propriedade slingResourceType
que indexa todo o conteúdo por sling:resourceType
.
Os índices de propriedade (ao contrário de Índices de propriedades Lucene) são melhor usados quando o query não é discernido por nodetype e uma única restrição de propriedade domina o conjunto de resultados.
Adicione a restrição de caminho mais estrita possível ao query. Por exemplo, prefira /content/my-site/us/en
em /content/my-site
ou /content/dam
em /
.
Query não otimizado
type=cq:Page
path=/content
property=jcr:content/contentType
property.value=article-page
Query otimizado
type=cq:Page
path=/content/my-site/us/en
property=jcr:content/contentType
property.value=article-page
O escopo da restrição de caminho de path=/content
para path=/content/my-site/us/en
permite que os índices reduzam o número de entradas de índice que precisam ser inspecionadas. Quando o query puder restringir muito bem o caminho, além de apenas /content
ou /content/dam
, verifique se o índice tem evaluatePathRestrictions=true
.
Observe que usar evaluatePathRestrictions
aumenta o tamanho do índice.
Sempre que possível, evite funções/operações do query, tais como: LIKE
e fn:XXXX
à medida que seus custos são dimensionados com o número de resultados baseados em restrições.
Query não otimizado
type=cq:Page
property=jcr:content/contentType
property.operation=like
property.value=%article%
Query otimizado
type=cq:Page
fulltext=article
fulltext.relPath=jcr:content/contentType
A condição LIKE é lenta para avaliação, pois nenhum índice pode ser usado se o texto start com um curinga ("%…'). A condição jcr:contains permite o uso de um índice de texto completo e, portanto, é preferida. Isso requer que o Índice de propriedades de Lucene resolvido tenha indexRule para jcr:content/contentType
com analayzed=true
.
O uso de funções de query como fn:lowercase(..)
pode ser mais difícil de otimizar, pois não há equivalentes mais rápidos (fora das configurações mais complexas e discretas do analisador de índice). É melhor identificar outras restrições de escopo para melhorar o desempenho geral do query, exigindo que as funções funcionem no menor conjunto possível de resultados potenciais.
Esse ajuste é específico do Construtor de Query e não se aplica a JCR-SQL2 ou XPath.
Use palavras-chave do Construtor de Query', oTotal quando o conjunto completo de resultados for não imediatamente necessário.
Query não otimizado
type=cq:Page
path=/content
Query otimizado
type=cq:Page
path=/content
p.guessTotal=100
Nos casos em que a execução do query é rápida, mas o número de resultados é grande, p. guessTotal
é uma otimização crítica para query do Construtor de Query.
p.guessTotal=100
instrui o Construtor de Query a coletar apenas os primeiros 100 resultados e a definir um sinalizador booleano indicando se existem pelo menos mais um resultado (mas não quantos mais, já que a contagem desse número resultaria em lentidão). Essa otimização se sobressai para paginação ou casos de uso de carregamento infinito, onde apenas um subconjunto de resultados é exibido incrementalmente.
Se o query ideal for resolvido para um Índice de propriedades, não haverá mais nada a ser feito, pois os Índices de propriedades são minimamente ajustáveis.
Caso contrário, o query deve resolver para um Índice de propriedades Lucene. Se nenhum índice puder ser resolvido, vá para Criar um novo índice.
Conforme necessário, converta o query em XPath ou JCR-SQL2.
Query Construtor de query
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 gerado do query Construtor de Query
/jcr:root/content/my-site/us/en//element(*, cq:Page)[jcr:content/@contentType = 'article-page'] order by jcr:content/@publishDate descending
Forneça o XPath (ou JCR-SQL2) para Oak Index Definition Generator gerar a definição otimizada do Índice de Propriedades do Lucene.
Definição do índice de propriedade do Lucene gerado
- 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"
Unir manualmente a definição gerada no Índice de propriedades do Lucene existente de forma aditiva. Tenha cuidado para não remover configurações existentes, pois elas podem ser usadas para satisfazer outros query.
/oak:index/cqPageLucene
.Verifique se o query não é resolvido para um Índice de propriedades do Lucene existente. Se isso acontecer, consulte a seção acima sobre ajuste e índice existente.
Conforme necessário, converta o query em XPath ou JCR-SQL2.
Query Construtor de query
type=myApp:Author
property=firstName
property.value=ira
XPath gerado do query Construtor de Query
//element(*, myApp:Page)[@firstName = 'ira']
Forneça o XPath (ou JCR-SQL2) para Oak Index Definition Generator gerar a definição otimizada do Índice de Propriedades do Lucene.
Definição do índice de propriedade do Lucene gerado
- compatVersion = 2
- type = "lucene"
- async = "async"
- jcr:primaryType = oak:QueryIndexDefinition
+ indexRules
+ myApp:AuthorModel
+ properties
+ firstName
- name = "firstName"
- propertyIndex = true
Implante a definição gerada do Índice de propriedades do Lucene.
Adicione a definição XML fornecida pelo Oak Index Definition Generator para o novo índice ao projeto AEM que gerencia as definições de índice Oak (lembre-se de tratar as definições de índice Oak como código, já que o código depende delas).
Implante e teste o novo índice após o ciclo de vida normal de desenvolvimento do software AEM e verifique se o query é resolvido no índice e se o query é executado.
Após a implantação inicial desse índice, AEM preencherá o índice com os dados necessários.
Devido à AEM arquitetura de conteúdo flexível, é difícil prever e garantir que os cruzamentos entre estruturas de conteúdo não evoluam ao longo do tempo para serem inaceitavelmente grandes.
Portanto, verifique se um índice atende aos query, exceto se a combinação de restrição de caminho e restrição de tipo de nó garantir que menos de 20 nós já tenham sido atravessados.
Depurador do Construtor de query
CRXDE Lite - Ferramenta Query
Registro do Construtor de query
DEBUG @ com.day.cq.search.impl.builder.QueryImpl
Registro de execução de query Oak
DEBUG @ org.apache.jackrabbit.oak.query
Configuração OSGi do mecanismo de Query do Apache Jackrabbit
NodeCounter JMX Mbean
Gerador de definição de índice Oak