AEM中有三种主要的慢查询分类,按严重性列出:
无索引查询
限制不当(或范围有限)的查询
大型结果集查询
查询的前两个分类(无索引和限制较差)较慢。 速度较慢,因为它们强制Oak查询引擎检查每个 潜在 要标识属于以下类别的结果(内容节点或索引项): 实际 结果集。
检查每个潜在结果的行为称为遍历。
由于必须检查每个潜在结果,所以确定实际结果集的成本与潜在结果数是线性增长的。
添加查询限制和调整索引允许以最佳格式存储索引数据,以便快速检索结果,并且减少或消除对潜在结果集的线性检查的需要。
在AEM 6.3中,默认情况下,当达到100,000的遍历时,查询失败并引发异常。 默认情况下,在AEM 6.3之前的AEM版本中不存在此限制,但可通过Apache Jackrabbit查询引擎设置OSGi配置和QueryEngineSettings JMX bean(属性LimitReads)进行设置。
说明 所有 查询并确保其查询计划不包含 /*遍历 解释一下。 遍历查询计划示例:
[nt:unstructured] as [a] /* traverse "/content//*" where ([a].[unindexedProperty] = 'some value') and (isdescendantnode([a], [/content])) */
监控 error.log
对于无索引遍历查询:
*INFO* org.apache.jackrabbit.oak.query.QueryImpl Traversal query (query without index) ... ; consider creating and index
解释所有查询并确保它们解析为调整索引以匹配查询的属性限制。
indexRules
用于所有属性限制,并且至少具有查询中最严格的属性限制。orderable=true.
cqPageLucene
没有索引规则 jcr:content/cq:tags
添加cq:tags索引规则之前
cq:tags索引规则
查询生成器查询
type=cq:Page
property=jcr:content/cq:tags
property.value=my:tag
查询计划
[cq:Page] as [a] /* lucene:cqPageLucene(/oak:index/cqPageLucene) *:* where [a].[jcr:content/cq:tags] = 'my:tag' */
此查询解析为 cqPageLucene
索引,但因为不存在属性索引规则 jcr:content
或 cq:tags
,当评估此限制时,中 cqPageLucene
检查索引以确定匹配项。 因此,如果索引包含100万 cq:Page
然后检查100万条记录以确定结果集。
添加cq:tags索引规则后
cq:tags索引规则
/oak:index/cqPageLucene/indexRules/cq:Page/properties/cqTags
@name=jcr:content/cq:tags
@propertyIndex=true
查询生成器查询
type=cq:Page
property=jcr:content/cq:tags
property.value=myTagNamespace:myTag
查询计划
[cq:Page] as [a] /* lucene:cqPageLucene(/oak:index/cqPageLucene) jcr:content/cq:tags:my:tag where [a].[jcr:content/cq:tags] = 'my:tag' */
添加的indexRule jcr:content/cq:tags
在 cqPageLucene
索引允许 cq:tags
以优化方式存储的数据。
当查询使用 jcr:content/cq:tags
执行限制,索引可以按值查找结果。 这意味着,如果100 cq:Page
节点具有 myTagNamespace:myTag
作为一个值,仅返回这100个结果,而其他999,000个结果则被排除在限制检查之外,使性能提高了10,000倍。
更多的查询限制会减少符合条件的结果集,并进一步优化查询优化。
同样,对于,没有额外的索引规则 cq:tags
属性,甚至是对具有限制的全文查询 cq:tags
将性能不佳,因为索引中的结果将返回所有全文匹配。 对cq:tags的限制将在它之后被过滤。
索引后过滤的另一个原因是访问控制列表,在开发过程中经常会遗漏。 请尝试确保查询未返回用户可能无法访问的路径。 可以通过更好的内容结构以及对查询提供相关的路径限制来完成此操作。
要确定Lucene索引是否返回了大量结果以返回一个小子集作为查询结果,一种有效的方法是启用DEBUG日志 org.apache.jackrabbit.oak.plugins.index.lucene.LucenePropertyIndex
. 这样,您就可以查看从索引加载了多少文档。 最终结果数量与加载的文档数量不应不成比例。 有关更多信息,请参阅 日志记录.
监控 error.log
对于遍历查询:
*WARN* org.apache.jackrabbit.oak.spi.query.Cursors$TraversingCursor Traversed ### nodes ... consider creating an index or changing the query
为oak.queryLimitInMemory(例如10000)和oak.queryLimitReads(例如5000)设置低阈值,并在遇到显示“查询读取超过x个节点……”的UnsupportedOperationException时优化代价高昂的查询
设置低阈值有助于避免资源密集型查询(即,不受任何索引支持或覆盖范围较少的索引支持)。 例如,读取一百万个节点的查询会导致大量IO,并对应用程序的整体性能产生负面影响。 因此,任何由于上述限制而失败的查询都应该进行分析和优化。
监测日志中触发大型节点遍历或大型栈内存消耗的查询: ”
*WARN* ... java.lang.UnsupportedOperationException: The query read or traversed more than 100000 nodes. To avoid affecting other tasks, processing was stopped.
监测日志中触发大型栈内存消耗的查询:
*WARN* ... java.lang.UnsupportedOperationException: The query read more than 500000 nodes in memory. To avoid running out of memory, processing was stopped
对于AEM 6.0 - 6.2版本,您可以通过AEM启动脚本中的JVM参数调整节点遍历的阈值,以防止大型查询使环境过载。 推荐值为:
-Doak.queryLimitInMemory=500000
-Doak.queryLimitReads=100000
在AEM 6.3中,上述两个参数默认已预配置,可以通过OSGi QueryEngineSettings进行修改。
有关详情,请参阅: https://jackrabbit.apache.org/oak/docs/query/query-engine.html#Slow_Queries_and_Read_Limits
AEM中查询性能优化的座右铭是:
“限制越多,越好。”
下面概述了为确保查询性能而建议的调整。 首先调整查询,这是一项不太引人注目的活动,然后如果需要,调整索引定义。
AEM支持以下查询语言:
以下示例使用Query Builder,它是AEM开发人员使用的最常见查询语言,但相同的原则适用于JCR-SQL2和XPath。
未优化查询
property=jcr:content/contentType
property.value=article-page
优化查询
type=cq:Page
property=jcr:content/contentType
property.value=article-page
缺少节点类型限制的查询强制AEM假设 nt:base
nodetype,AEM中的每个节点都是其子类型,这实际上不会导致任何节点类型限制。
设置 type=cq:Page
将此查询限制为仅 cq:Page
节点,并将查询解析为AEM cqPageLucene,将结果限制为节点的子集(仅限 cq:Page
AEM节点)。
未优化查询
type=nt:hierarchyNode
property=jcr:content/contentType
property.value=article-page
优化查询
type=cq:Page
property=jcr:content/contentType
property.value=article-page
nt:hierarchyNode
是父节点类型 cq:Page
. 假设 jcr:content/contentType=article-page
仅应用于 cq:Page
节点通过Adobe的自定义应用程序,此查询仅返回 cq:Page
节点位置 jcr:content/contentType=article-page
. 但是,此流量不是最佳限制,因为:
nt:hierarchyNode
(例如, dam:Asset
)将不必要地添加到潜在结果集。nt:hierarchyNode
,但是,由于为 cq:Page
.设置 type=cq:Page
将此查询限制为仅 cq:Page
节点,并将查询解析为AEM cqPageLucene,将结果限制为AEM中的节点子集(仅限cq:Page节点)。
未优化查询
property=jcr:content/contentType
property.value=article-page
优化查询
property=jcr:content/sling:resourceType
property.value=my-site/components/structure/article-page
更改属性限制 jcr:content/contentType
(自定义值)到已知属性 sling:resourceType
允许查询解析为属性索引 slingResourceType
用于索引所有内容 sling:resourceType
.
当查询未按节点类型识别,并且单个属性限制主导结果集时,最好使用属性索引(与Lucene属性索引相对)。
/content/my-site/us/en
超过 /content/my-site
,或 /content/dam
超过 /
.未优化查询
type=cq:Page
path=/content
property=jcr:content/contentType
property.value=article-page
优化查询
type=cq:Page
path=/content/my-site/us/en
property=jcr:content/contentType
property.value=article-page
从以下位置设定路径限制的范围 path=/content
到 path=/content/my-site/us/en
允许索引减少必须检查的索引项数。 当查询可以很好地限制路径时,不仅仅是 /content
或 /content/dam
,确保索引具有 evaluatePathRestrictions=true
.
使用注释 evaluatePathRestrictions
会增加索引大小。
LIKE
和 fn:XXXX
成本会随着基于限制的结果数量而扩展。未优化查询
type=cq:Page
property=jcr:content/contentType
property.operation=like
property.value=%article%
优化查询
type=cq:Page
fulltext=article
fulltext.relPath=jcr:content/contentType
LIKE条件的计算速度较慢,因为如果文本以通配符(“%。…”)开头,则无法使用索引。 jcr:contains条件允许使用全文索引,因此是首选。 它要求解析的Lucene属性索引具有indexRule jcr:content/contentType
替换为 analayzed=true
.
使用查询函数,如 fn:lowercase(..)
由于没有速度更快的对等项(在更复杂和侵入性更强的索引分析器配置之外),可能更难以优化。 最好确定其他范围限制以提高整体查询性能,要求函数尽可能对最小的潜在结果集进行操作。
此调整特定于查询生成器,不适用于JCR-SQL2或XPath。
使用 查询生成器的guessTotal 当整组结果为 非 立即需要。
未优化查询
type=cq:Page
path=/content
优化查询
type=cq:Page
path=/content
p.guessTotal=100
对于查询执行速度较快但结果数量较大的情况,p。 guessTotal
是Query Builder查询的关键优化。
p.guessTotal=100
告知Query Builder仅收集前100个结果。 并且,设置一个布尔标记,指示是否至少还有一个结果(但不指示还有多少个结果,因为计数此数字会导致速度变慢)。 此优化不适用于分页或无限加载用例,在这些用例中,仅增量显示一个结果子集。
如果最佳查询解析为属性索引,则由于属性索引最低可调整,因此没有其他可执行的操作。
否则,查询应解析为Lucene属性索引。 如果无法解析任何索引,请跳转到创建新索引。
根据需要,将查询转换为XPath或JCR-SQL2。
查询生成器查询
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
从Query Builder查询生成的XPath
/jcr:root/content/my-site/us/en//element(*, cq:Page)[jcr:content/@contentType = 'article-page'] order by jcr:content/@publishDate descending
将XPath(或JCR-SQL2)提供给Oak索引定义生成器,位于 https://oakutils.appspot.com/generate/index
以便生成优化的Lucene属性索引定义。
生成的Lucene属性索引定义
- 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"
以累加方式手动将生成的定义合并到现有Lucene属性索引中。 请注意不要删除现有配置,因为它们可能用于满足其他查询。
/oak:index/cqPageLucene
.验证查询是否未解析为现有的Lucene属性索引。 如果是这样,请参阅上面关于优化和现有索引的部分。
根据需要,将查询转换为XPath或JCR-SQL2。
查询生成器查询
type=myApp:Author
property=firstName
property.value=ira
从Query Builder查询生成的XPath
//element(*, myApp:Page)[@firstName = 'ira']
将XPath(或JCR-SQL2)提供给Oak索引定义生成器,位于 https://oakutils.appspot.com/generate/index
以便生成优化的Lucene属性索引定义。
生成的Lucene属性索引定义
- compatVersion = 2
- type = "lucene"
- async = "async"
- jcr:primaryType = oak:QueryIndexDefinition
+ indexRules
+ myApp:AuthorModel
+ properties
+ firstName
- name = "firstName"
- propertyIndex = true
部署生成的Lucene属性索引定义。
将Oak索引定义生成器为新索引提供的XML定义添加到管理Oak索引定义的AEM项目中(请记住,将Oak索引定义视为代码,因为代码依赖于它们)。
在通常的AEM软件开发生命周期内部署和测试新索引,并验证查询是否解析为索引以及查询是否有效。
初始部署此索引时,AEM会使用所需数据填充该索引。
由于AEM灵活的内容架构,很难预测并确保内容结构的遍历不会随时间演进而变得异常庞大。
因此,确保索引满足查询,除非路径限制和节点类型限制的组合确保 曾经遍历的节点少于20个。
Query Builder调试器
CRXDE Lite — 查询工具
查询生成器日志记录
DEBUG @ com.day.cq.search.impl.builder.QueryImpl
Oak查询执行日志记录
DEBUG @ org.apache.jackrabbit.oak.query
Apache Jackrabbit查询引擎设置OSGi配置
NodeCounter JMX Mbean
Oak索引定义生成器位于https://oakutils.appspot.com/generate/index