在 Neptune 中使用小精灵 explain API - Amazon Neptune

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

在 Neptune 中使用小精灵 explain API

Amazon Neptune Gremlin explain API 返回运行指定查询时将执行的查询计划。由于实际上API并未运行查询,因此计划几乎是即时返回的。

它与 TinkerPop .explain () 步骤的不同之处在于能够报告特定于 Neptune 引擎的信息。

Gremlin explain 报告中包含的信息

explain 报告包含以下信息:

  • 要求的查询字符串。

  • 原始遍历。这是通过将查询字符串解析为步骤而生成的 TinkerPop Traversal 对象。 TinkerPop 它等同于在针对的查询.explain()上运行所产生的原始查询 TinkerPop TinkerGraph。

  • 转换的遍历。这是通过将遍历转换为海王星逻辑查询计划表示形式而生成的 Neptune Trav TinkerPop ersal。在许多情况下,整个 TinkerPop 遍历会转换为两个 Neptune 步骤:一个执行整个查询 (NeptuneGraphQueryStep),另一个将海王星查询引擎的输出转换回 Traversers ()。 TinkerPop NeptuneTraverserConverterStep

  • 优化的遍历。这是 Neptune 查询计划的优化版本,已经通过一系列静态减负优化器运行它,这些优化器基于静态分析和估计基数重写查询。优化器执行多种操作,例如根据范围计数对运算符进行重新排序、删除不必要或多余的运算符、重新排列筛选条件、将运算符推入不同的组等。

  • 谓词计数。如前所述,出于 Neptune 索引策略的原因,具有大量不同谓词可能会导致性能问题。对于使用没有边缘标签(.in.both)的反向遍历运算符的查询,这一点尤为明显。如果使用了此类运算符并且谓词数量足够多,explain 报告将显示警告消息。

  • DFE信息。启用DFE备用引擎后,以下遍历组件可能会出现在优化的遍历中:

    • DFEStep— Neptune 优化的遍历DFE步骤,其中包含一个孩子。DFENode DFEStep表示查询计划中在DFE引擎中执行的部分。

    • DFENode – 包含中间表示(作为一个或多个子 DFEJoinGroupNodes)。

    • DFEJoinGroupNode – 表示一个或多个 DFENodeDFEJoinGroupNode 元素的联接。

    • NeptuneInterleavingStep— Neptune 优化的遍历DFE步骤,其中包含一个孩子。DFEStep

      还包含一个 stepInfo 元素,其中包含有关遍历的信息,例如边界元素、使用的路径元素等。此信息用于处理子 DFEStep

    要确定您的查询是否正在接受评估,一种简单的方法DFE是检查explain输出是否包含DFEStep。任何不属于的遍历部分都不会由引DFEStep擎执行DFE,而是由引擎执行。 TinkerPop

    有关示例报告,请参阅DFE启用的示例

Gremlin explain 语法

的语法与 for 查询HTTPAPI的语法相同,唯一的不同explainAPI是它/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 原生处理 GraphStepVertexStep,但如果引入 FoldStepUnfoldStep,则结果 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 的查询示例 full-text-search

以下查询使用 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} } }]

启用explain时使用的示例 DFE

以下是启用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

explain 中的信息有关报告中DFE特定部分的说明,请参阅。