在 Neptune 中使用 Gremlin explain API
Amazon Neptune Gremlin explain API 返回将在运行指定查询时执行的查询计划。由于该 API 实际上并未运行查询,因此几乎可即时返回计划。
它与 TinkerPop .explain() 步骤不同,以便能够报告特定于 Neptune 引擎的信息。
Gremlin explain 报告中包含的信息
explain 报告包含以下信息:
要求的查询字符串。
原始遍历。这是通过将查询字符串解析为 TinkerPop 步骤而生成的 TinkerPop 遍历对象。它等同于针对 TinkerPop TinkerGraph 在查询上运行
.explain()所生成的原始查询。转换的遍历。这是通过将 TinkerPop 遍历转换为 Neptune 逻辑查询计划表示形式而生成的 Neptune 遍历。在许多情况下,整个 TinkerPop 遍历都将转换为两个 Neptune 步骤:一个步骤执行整个查询 (
NeptuneGraphQueryStep),另一个步骤将 Neptune 查询引擎输出转换回 TinkerPop Traverser (NeptuneTraverserConverterStep)。优化的遍历。这是 Neptune 查询计划的优化版本,已经通过一系列静态减负优化器运行它,这些优化器基于静态分析和估计基数重写查询。优化器执行多种操作,例如根据范围计数对运算符进行重新排序、删除不必要或多余的运算符、重新排列筛选条件、将运算符推入不同的组等。
谓词计数。如前所述,出于 Neptune 索引策略的原因,具有大量不同谓词可能会导致性能问题。对于使用没有边缘标签(
.in或.both)的反向遍历运算符的查询,这一点尤为明显。如果使用了此类运算符并且谓词数量足够多,explain报告将显示警告消息。-
DFE 信息。启用 DFE 替代引擎后,以下遍历组件可能会出现在优化的遍历中:
-
DFEStep– 在包含子DFENode的遍历中,Neptune 优化的 DFE 步骤。DFEStep表示查询计划中在 DFE 引擎中执行的部分。 -
DFENode–包含中间表示(作为一个或多个子DFEJoinGroupNodes)。 -
DFEJoinGroupNode– 表示一个或多个DFENode或DFEJoinGroupNode元素的联接。 -
NeptuneInterleavingStep– 在包含子DFEStep的遍历中,Neptune 优化的 DFE 步骤。还包含一个
stepInfo元素,其中包含有关遍历的信息,例如边界元素、使用的路径元素等。此信息用于处理子DFEStep。
要查明 DFE 是否正在计算您的查询,一种简单的方法是检查
explain输出中是否包含DFEStep。任何不属于DFEStep的遍历部分都不会由 DFE 执行,而是由 TinkerPop 引擎执行。有关示例报告,请参阅启用 DFE 的示例。
-
Gremlin explain 语法
explain API 的语法与查询的 HTTP API 相同,不同之处在于,它使用 /gremlin/explain 作为终端节点而不是 /gremlin,如以下示例所示。
curl -X POST https://your-neptune-endpoint:port/gremlin/explain -d '{"gremlin":"g.V().limit(1)"}'
前面的查询将产生以下输出。
******************************************************* Neptune Gremlin Explain ******************************************************* Query String ============ g.V().limit(1) Original Traversal ================== [GraphStep(vertex,[]), RangeGlobalStep(0,1)] Converted Traversal =================== Neptune steps: [ NeptuneGraphQueryStep(Vertex) { JoinGroupNode { PatternNode[(?1, <~label>, ?2, <~>) . project distinct ?1 .] }, finishers=[limit(1)], annotations={path=[Vertex(?1):GraphStep], maxVarId=3} }, NeptuneTraverserConverterStep ] Optimized Traversal =================== Neptune steps: [ NeptuneGraphQueryStep(Vertex) { JoinGroupNode { PatternNode[(?1, <~label>, ?2, <~>) . project distinct ?1 .], {estimatedCardinality=INFINITY} }, finishers=[limit(1)], annotations={path=[Vertex(?1):GraphStep], maxVarId=3} }, NeptuneTraverserConverterStep ] Predicates ========== # of predicates: 18
未转换的 TinkerPop 步骤
理想情况下,遍历中的所有 TinkerPop 步骤都有对应的原生 Neptune 运算符。如果不是这种情况,Neptune 将回退到 TinkerPop 步骤执行,以解决运算符缺失问题。如果遍历使用 Neptune 尚无原生运算符的步骤,explain 报告将显示一条警告,指出缺失运算符的位置。
当遇到没有对应的原生 Neptune 运算符的步骤时,即使后续步骤有对应的原生 Neptune 运算符,也将使用 TinkerPop 步骤来运行从该点开始的整个遍历。
这种情况的例外是调用 Neptune 全文搜索时。NeptuneSearchStep 实施的步骤没有全文搜索步骤的本机等效运算符。
查询中的所有步骤都具有原生等效运算符的 explain 输出示例
下面是一个示例查询 explain 报告,其中所有步骤都有原生等效运算符:
******************************************************* Neptune Gremlin Explain ******************************************************* Query String ============ g.V().out() Original Traversal ================== [GraphStep(vertex,[]), VertexStep(OUT,vertex)] Converted Traversal =================== Neptune steps: [ NeptuneGraphQueryStep(Vertex) { JoinGroupNode { PatternNode[(?1, <~label>, ?2, <~>) . project distinct ?1 .] PatternNode[(?1, ?5, ?3, ?6) . project ?1,?3 . IsEdgeIdFilter(?6) .] PatternNode[(?3, <~label>, ?4, <~>) . project ask .] }, annotations={path=[Vertex(?1):GraphStep, Vertex(?3):VertexStep], maxVarId=7} }, NeptuneTraverserConverterStep ] Optimized Traversal =================== Neptune steps: [ NeptuneGraphQueryStep(Vertex) { JoinGroupNode { PatternNode[(?1, ?5, ?3, ?6) . project ?1,?3 . IsEdgeIdFilter(?6) .], {estimatedCardinality=INFINITY} }, annotations={path=[Vertex(?1):GraphStep, Vertex(?3):VertexStep], maxVarId=7} }, NeptuneTraverserConverterStep ] Predicates ========== # of predicates: 18
查询中的部分步骤没有本机等效运算符的示例
Neptune 原生处理 GraphStep 和 VertexStep,但如果引入 FoldStep 和 UnfoldStep,则结果 explain 输出会有所不同:
******************************************************* Neptune Gremlin Explain ******************************************************* Query String ============ g.V().fold().unfold().out() Original Traversal ================== [GraphStep(vertex,[]), FoldStep, UnfoldStep, VertexStep(OUT,vertex)] Converted Traversal =================== Neptune steps: [ NeptuneGraphQueryStep(Vertex) { JoinGroupNode { PatternNode[(?1, <~label>, ?2, <~>) . project distinct ?1 .] }, annotations={path=[Vertex(?1):GraphStep], maxVarId=3} }, NeptuneTraverserConverterStep ] + not converted into Neptune steps: [FoldStep, UnfoldStep, VertexStep(OUT,vertex)] Optimized Traversal =================== Neptune steps: [ NeptuneGraphQueryStep(Vertex) { JoinGroupNode { PatternNode[(?1, <~label>, ?2, <~>) . project distinct ?1 .], {estimatedCardinality=INFINITY} }, annotations={path=[Vertex(?1):GraphStep], maxVarId=3} }, NeptuneTraverserConverterStep, NeptuneMemoryTrackerStep ] + not converted into Neptune steps: [FoldStep, UnfoldStep, VertexStep(OUT,vertex)] WARNING: >> FoldStep << is not supported natively yet
在这种情况下,FoldStep 将使遍历跳出本机执行。同时,后续的 VertexStep 也不再在本机处理,因为它出现在 Fold/Unfold 步骤之后。
出于性能和成本考虑,请在编制遍历时让尽可能多的工作在 Neptune 查询引擎内部原生进行,而不是通过 TinkerPop 步骤实现来处理。
使用 Neptune 全文搜索的查询示例
以下查询使用 Neptune 全文搜索:
g.withSideEffect("Neptune#fts.endpoint", "some_endpoint") .V() .tail(100) .has("Neptune#fts mark*") ------- .has("name", "Neptune#fts mark*") .has("Person", "name", "Neptune#fts mark*")
.has("name", "Neptune#fts mark*") 部分将搜索限制为带有 name 的顶点,而 .has("Person", "name", "Neptune#fts mark*") 将搜索限制为带有 name 和标签 Person 的顶点。这将导致 explain 报告中出现以下遍历:
Final Traversal [NeptuneGraphQueryStep(Vertex) { JoinGroupNode { PatternNode[(?1, termid(1,URI), ?2, termid(0,URI)) . project distinct ?1 .], {estimatedCardinality=INFINITY} }, annotations={path=[Vertex(?1):GraphStep], maxVarId=4} }, NeptuneTraverserConverterStep, NeptuneTailGlobalStep(10), NeptuneTinkerpopTraverserConverterStep, NeptuneSearchStep { JoinGroupNode { SearchNode[(idVar=?3, query=mark*, field=name) . project ask .], {endpoint=some_endpoint} } JoinGroupNode { SearchNode[(idVar=?3, query=mark*, field=name) . project ask .], {endpoint=some_endpoint} } }]
启用 DFE 时使用 explain 的示例
以下是启用 DFE 备用查询引擎时 explain 报告的示例:
******************************************************* Neptune Gremlin Explain ******************************************************* Query String ============ g.V().as("a").out().has("name", "josh").out().in().where(eq("a")) Original Traversal ================== [GraphStep(vertex,[])@[a], VertexStep(OUT,vertex), HasStep([name.eq(josh)]), VertexStep(OUT,vertex), VertexStep(IN,vertex), WherePredicateStep(eq(a))] Converted Traversal =================== Neptune steps: [ DFEStep(Vertex) { DFENode { DFEJoinGroupNode[ children={ DFEPatternNode[(?1, <http://www.w3.org/1999/02/22-rdf-syntax-ns#type>, ?2, <http://aws.amazon.com/neptune/vocab/v01/DefaultNamedGraph>) . project DISTINCT[?1] {rangeCountEstimate=unknown}], DFEPatternNode[(?1, ?3, ?4, ?5) . project ALL[?1, ?4] graphFilters=(!= <http://aws.amazon.com/neptune/vocab/v01/DefaultNamedGraph> . ), {rangeCountEstimate=unknown}] }, {rangeCountEstimate=unknown} ] } [Vertex(?1):GraphStep@[a], Vertex(?4):VertexStep] } , NeptuneTraverserConverterDFEStep ] + not converted into Neptune steps: HasStep([name.eq(josh)]), Neptune steps: [ NeptuneInterleavingStep { StepInfo[joinVars=[?7, ?1], frontierElement=Vertex(?7):HasStep, pathElements={a=(last,Vertex(?1):GraphStep@[a])}, listPathElement={}, indexTime=0ms], DFEStep(Vertex) { DFENode { DFEJoinGroupNode[ children={ DFEPatternNode[(?7, ?8, ?9, ?10) . project ALL[?7, ?9] graphFilters=(!= <http://aws.amazon.com/neptune/vocab/v01/DefaultNamedGraph> . ), {rangeCountEstimate=unknown}], DFEPatternNode[(?12, ?11, ?9, ?13) . project ALL[?9, ?12] graphFilters=(!= <http://aws.amazon.com/neptune/vocab/v01/DefaultNamedGraph> . ), {rangeCountEstimate=unknown}] }, {rangeCountEstimate=unknown} ] } [Vertex(?9):VertexStep, Vertex(?12):VertexStep] } } ] + not converted into Neptune steps: WherePredicateStep(eq(a)), Neptune steps: [ DFECleanupStep ] Optimized Traversal =================== Neptune steps: [ DFEStep(Vertex) { DFENode { DFEJoinGroupNode[ children={ DFEPatternNode[(?1, ?3, ?4, ?5) . project ALL[?1, ?4] graphFilters=(!= defaultGraph[526] . ), {rangeCountEstimate=9223372036854775807}] }, {rangeCountEstimate=unknown} ] } [Vertex(?1):GraphStep@[a], Vertex(?4):VertexStep] } , NeptuneTraverserConverterDFEStep ] + not converted into Neptune steps: NeptuneHasStep([name.eq(josh)]), Neptune steps: [ NeptuneMemoryTrackerStep, NeptuneInterleavingStep { StepInfo[joinVars=[?7, ?1], frontierElement=Vertex(?7):HasStep, pathElements={a=(last,Vertex(?1):GraphStep@[a])}, listPathElement={}, indexTime=0ms], DFEStep(Vertex) { DFENode { DFEJoinGroupNode[ children={ DFEPatternNode[(?7, ?8, ?9, ?10) . project ALL[?7, ?9] graphFilters=(!= defaultGraph[526] . ), {rangeCountEstimate=9223372036854775807}], DFEPatternNode[(?12, ?11, ?9, ?13) . project ALL[?9, ?12] graphFilters=(!= defaultGraph[526] . ), {rangeCountEstimate=9223372036854775807}] }, {rangeCountEstimate=unknown} ] } [Vertex(?9):VertexStep, Vertex(?12):VertexStep] } } ] + not converted into Neptune steps: WherePredicateStep(eq(a)), Neptune steps: [ DFECleanupStep ] WARNING: >> [NeptuneHasStep([name.eq(josh)]), WherePredicateStep(eq(a))] << (or one of the children for each step) is not supported natively yet Predicates ========== # of predicates: 8
有关报告中特定于 DFE 的部分的说明,请参阅explain 中的信息。