本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
使用调整 Gremlin 查询explain
和profile
您经常可以使用从 Neptune 获得的报告中提供的信息,在 Amazon Neptune 中调整 Gremlin 查询以获得更好的性能说明和轮廓API。要做到这一点,有助于了解 Neptune 如何处理 Gremlin 遍历。
在中进行了更改 TinkerPop 3.4.11 版提高了查询处理方式的正确性,但目前有时会严重影响查询性能。
例如,这种查询的运行速度可能会慢得多:
g.V().hasLabel('airport'). order(). by(out().count(),desc). limit(10). out()
限制步骤之后的顶点现在以非最佳方式获取,因为 TinkerPop 3.4.11 更改。为避免这种情况,您可以通过在order().by()
. 例如:
g.V().hasLabel('airport'). order(). by(out().count(),desc). limit(10). barrier(). out()
TinkerPop 3.4.11 已在 Neptune 启用引擎版本 1.0.5.0.
了解 Neptune 中的 Gremlin 遍历处理
当 Gremlin 遍历被发送到 Neptune 时,有三个主要流程可以将遍历转换为基础执行计划以供引擎执行。这些都是解析、转换和优化:

遍历解析过程
处理遍历的第一步是将其解析成一种公共语言。在 Neptune 中,那种共同语言是一套 TinkerPop 属于的步骤TinkerPop API
您可以将 Gremlin 遍历发送给 Neptune,既可以作为字符串也可以作为字节码发送。REST 端点和 Java 客户端驱动程序submit()
方法将遍历作为字符串发送,如下例所示:
client.submit("g.V()")
应用程序和语言驱动程序Gremlin 语言变体 (GLV)
遍历转换过程
处理遍历的第二步是将其转换为 TinkerPop 步骤转换成一组转换后和未转换的 Neptune 步骤。Apache 中的大多数步骤 TinkerPop Gremlin 查询语言转换为 Neptune 特定步骤,这些步骤经过优化,可在底层 Neptune 引擎上运行。当 TinkerPop 在遍历中遇到没有 Neptune 等效值的步骤,该步骤以及遍历中的所有后续步骤都由 TinkerPop 查询引擎。
有关在何种情况下可转换哪些步骤的更多信息,请参阅。Grimlin 步骤支持.
遍历优化过程
遍历处理的最后一步是通过优化程序运行一系列转换和未转换的步骤,以尝试确定最佳的执行计划。此优化的结果是 Neptune 引擎处理的执行计划。
使用 Neptune Gemlinexplain
用于调整查询的 API
Neptune 解释 API 与 Gremlin 不一样explain()
步骤。它返回 Neptune 引擎在执行查询时将处理的最终执行计划。由于它不执行任何处理,因此无论使用的参数如何,它都会返回相同的计划,且其输出不包含有关实际执行情况的统计信息。
考虑以下简单的穿越,它可以找到安克雷奇的所有机场顶点:
g.V().has('code','ANC')
有两种方法可以通过 Neptune 运行此遍历。explain
API。第一种方法是对解释端点进行 REST 调用,如下所示:
curl -X POST https://
your-neptune-endpoint
:port
/gremlin/explain -d '{"gremlin":"g.V().has('code','ANC')"}'
第二种方法是使用 Neptune 工作台%%gremlin使用细胞魔法explain
参数。这将细胞体内包含的穿越传递给 Neptuneexplain
API 然后在运行单元格时显示结果输出:
%%gremlin explain g.V().has('code','ANC')
在任一情况下,结果结果explain
API 输出描述了海王星穿越的执行计划。如下图所示,该计划包括处理管道中 3 个步骤中的每个步骤:

通过查看未转换的步骤来调整遍历
Neptune 首先要寻找的东西之一explain
API 输出适用于未转换为 Neptune 原生步骤的 Gremlin 步骤。在查询计划中,遇到无法转换为 Neptune 本机步骤的步骤时,该步骤以及计划中的所有后续步骤都将由 Gremlin 服务器处理。
在以上示例中,遍历中的所有步骤均转换。让我们来看看explain
此遍历的 API 输出:
g.V().has('code','ANC').out().choose(hasLabel('airport'), values('code'), constant('Not an airport'))
正如你在下图中看到的那样,Neptune 无法转换choose()
步骤:

您可以做几件事来优化遍历的性能。首先是以消除无法转换的步骤的方式重写它。另一个是将步骤移动到遍历的结束,以便所有其他步骤都可以转换为本地步骤。
包含未转换步骤的查询计划并不总是需要调整。如果无法转换的步骤处于遍历的末尾,并且与输出的格式相关,而不是如何遍历图表,那么它们可能对性能没有什么影响。
在检查 Neptune 的输出时要注意的另一件事explain
API 是不使用索引的步骤。以下穿越查找所有有航班降落在安克雷奇的机场:
g.V().has('code','ANC').in().values('code')
此遍历的解释 API 的输出为:
******************************************************* Neptune Gremlin Explain ******************************************************* Query String ============ g.V().has('code','ANC').in().values('code') Original Traversal ================== [GraphStep(vertex,[]), HasStep([code.eq(ANC)]), VertexStep(IN,vertex), PropertiesStep([code],value)] Converted Traversal =================== Neptune steps: [ NeptuneGraphQueryStep(PropertyValue) { JoinGroupNode { PatternNode[(?1, <~label>, ?2, <~>) . project distinct ?1 .] PatternNode[(?1, <code>, "ANC", ?) . project ask .] PatternNode[(?3, ?5, ?1, ?6) . project ?1,?3 . IsEdgeIdFilter(?6) .] PatternNode[(?3, <~label>, ?4, <~>) . project ask .] PatternNode[(?3, ?7, ?8, <~>) . project ?3,?8 . ContainsFilter(?7 in (<code>)) .] }, annotations={path=[Vertex(?1):GraphStep, Vertex(?3):VertexStep, PropertyValue(?8):PropertiesStep], maxVarId=9} }, NeptuneTraverserConverterStep ] Optimized Traversal =================== Neptune steps: [ NeptuneGraphQueryStep(PropertyValue) { JoinGroupNode { PatternNode[(?1, <code>, "ANC", ?) . project ?1 .], {estimatedCardinality=1} PatternNode[(?3, ?5, ?1, ?6) . project ?1,?3 . IsEdgeIdFilter(?6) .], {estimatedCardinality=INFINITY} PatternNode[(?3, ?7=<code>, ?8, <~>) . project ?3,?8 .], {estimatedCardinality=7564} }, annotations={path=[Vertex(?1):GraphStep, Vertex(?3):VertexStep, PropertyValue(?8):PropertiesStep], maxVarId=9} }, NeptuneTraverserConverterStep ] Predicates ========== # of predicates: 26 WARNING: reverse traversal with no edge label(s) - .in() / .both() may impact query performance
这些区域有:WARNING
出现输出底部的消息是因为in()
无法使用 Neptune 维护的 3 个索引中的一个处理遍历中的步骤(请参阅Neptune 中的语句如何编制索引和Gremlin 在 Neptune 中的声明)。由于in()
step 不包含边缘过滤器,无法使用SPOG
、POGS
要么GPSO
索引。相反,Neptune 必须执行联合扫描才能找到请求的顶点,这样的效率要低得多。
在这种情况下,有两种方法可以调整遍历。首先是将一个或多个筛选条件添加到in()
步骤,以便可以使用索引查找来解析查询。对于上述示例,可能是:
g.V().has('code','ANC').in('route').values('code')
Neptune 的输出explain
修订后遍历的 API 不再包含WARNING
消息:
******************************************************* Neptune Gremlin Explain ******************************************************* Query String ============ g.V().has('code','ANC').in('route').values('code') Original Traversal ================== [GraphStep(vertex,[]), HasStep([code.eq(ANC)]), VertexStep(IN,[route],vertex), PropertiesStep([code],value)] Converted Traversal =================== Neptune steps: [ NeptuneGraphQueryStep(PropertyValue) { JoinGroupNode { PatternNode[(?1, <~label>, ?2, <~>) . project distinct ?1 .] PatternNode[(?1, <code>, "ANC", ?) . project ask .] PatternNode[(?3, ?5, ?1, ?6) . project ?1,?3 . IsEdgeIdFilter(?6) . ContainsFilter(?5 in (<route>)) .] PatternNode[(?3, <~label>, ?4, <~>) . project ask .] PatternNode[(?3, ?7, ?8, <~>) . project ?3,?8 . ContainsFilter(?7 in (<code>)) .] }, annotations={path=[Vertex(?1):GraphStep, Vertex(?3):VertexStep, PropertyValue(?8):PropertiesStep], maxVarId=9} }, NeptuneTraverserConverterStep ] Optimized Traversal =================== Neptune steps: [ NeptuneGraphQueryStep(PropertyValue) { JoinGroupNode { PatternNode[(?1, <code>, "ANC", ?) . project ?1 .], {estimatedCardinality=1} PatternNode[(?3, ?5=<route>, ?1, ?6) . project ?1,?3 . IsEdgeIdFilter(?6) .], {estimatedCardinality=32042} PatternNode[(?3, ?7=<code>, ?8, <~>) . project ?3,?8 .], {estimatedCardinality=7564} }, annotations={path=[Vertex(?1):GraphStep, Vertex(?3):VertexStep, PropertyValue(?8):PropertiesStep], maxVarId=9} }, NeptuneTraverserConverterStep ] Predicates ========== # of predicates: 26
如果您正在运行许多此类遍历,另一种选择是在具有可选OSGP
已启用索引(请参阅启用 OSGP 索引)。启用OSGP
指数有缺点:
在加载任何数据之前,它必须在数据库集群中启用。
顶点和边缘的插入速率最多可能会降低 23%。
存储使用量将增加约 20%。
将请求分散到所有索引的读取查询可能会增加延迟。
拥有OSGP
index 对于一组受限的查询模式来说很有意义,但除非你频繁运行这些模式,否则通常最好尝试确保可以使用三个主索引来解析你编写的遍历。
使用大量谓词
Neptune 将图表中的每个边缘标签和每个不同的顶点或边缘属性名称视为谓词,默认情况下设计用于处理相对较少的不同谓词。当你的图表数据中有几千个谓词时,性能可能会降低。
Neptuneexplain
如果是这种情况,输出会警告你:
Predicates ========== # of predicates: 9549 WARNING: high predicate count (# of distinct property names and edge labels)
如果重新制作数据模型以减少标签和属性的数量,从而减少谓词的数量并不方便,那么调整遍历的最佳方法是在具有OSGP
如上所述,已启用索引。
使用 Neptune Gemlinprofile
用于调整遍历的 API
Neptuneprofile
API 与 Gremlin 完全不同profile()
步骤。像explain
API,其输出包括 Neptune 引擎在执行遍历时使用的查询计划。此外,profile
根据遍历参数的设置方式,输出包括遍历的实际执行统计信息。
再次,进行简单的穿越,找到安克雷奇的所有机场顶点:
g.V().has('code','ANC')
与explain
API,可以调用profile
使用 REST 调用的 API:
curl -X POST https://
your-neptune-endpoint
:port
/gremlin/profile -d '{"gremlin":"g.V().has('code','ANC')"}'
你也可以使用 Neptune 工作台%%gremlin使用细胞魔法profile
参数。这将细胞体内包含的穿越传递给 Neptuneprofile
API 然后在运行单元格时显示结果输出:
%%gremlin profile g.V().has('code','ANC')
在任一情况下,结果结果profile
如下图所示,API 输出包含 Neptune 的遍历执行计划和计划执行情况的统计信息:

Inprofile
输出时,执行计划部分仅包含遍历的最终执行计划,而不包含中间步骤。管道部分包含执行的物理管道操作以及遍历执行所花费的实际时间(以毫秒为单位)。运行时指标对于比较两个不同版本的遍历版本在优化它们时所花费的时间非常有用。
遍历的初始运行时通常比后续运行时长,因为第一个运行时会导致缓存相关数据。
的第三部分profile
输出包含执行统计信息和遍历的结果。要了解这些信息在调整穿越过程中如何有用,请考虑以下遍历,它会查找每个名称以 “Anchora” 开头的机场,以及从这些机场两跳到的所有机场、返回机场代码、航班路线和距离:
%%gremlin profile g.withSideEffect("Neptune#fts.endpoint", "{your-OpenSearch-endpoint-URL"). V().has("city", "Neptune#fts Anchora~"). repeat(outE('route').inV().simplePath()).times(2). project('Destination', 'Route'). by('code'). by(path().by('code').by('dist'))
Neptune 中的穿越指标profile
API 输出
全部可用的第一组指标profile
输出是遍历指标。这些类似于 Gremlinprofile()
步骤指标,但有几个区别:
Traversal Metrics ================= Step Count Traversers Time (ms) % Dur ------------------------------------------------------------------------------------------------------------- NeptuneGraphQueryStep(Vertex) 3856 3856 91.701 9.09 NeptuneTraverserConverterStep 3856 3856 38.787 3.84 ProjectStep([Destination, Route],[value(code), ... 3856 3856 878.786 87.07 PathStep([value(code), value(dist)]) 3856 3856 601.359 >TOTAL - - 1009.274 -
遍历度量表的第一列列列出了遍历执行的步骤。前两个步骤通常是海王星特定的步骤,NeptuneGraphQueryStep
和NeptuneTraverserConverterStep
.
NeptuneGraphQueryStep
表示可以由 Neptune 引擎本地转换和执行的整个遍历部分的执行时间。
NeptuneTraverserConverterStep
表示将这些转换后的步骤的输出转换为的过程 TinkerPop 遍历器允许处理无法转换的步骤(如果有),或者以兼容 TinkerPOP 的格式返回结果的步骤。
在上面的例子中,我们有几个未转换的步骤,因此我们可以看到每个步骤 TinkerPop 步骤 (ProjectStep
、PathStep
) 然后在表格中显示为一行。
表中的第二栏,Count
,报告的数量代表的通过该步骤的遍历器,而第三列Traversers
,报告通过该步骤的遍历者数量,如TinkerPop 个人资料步骤文档
在我们的例子中,有 3,856 个顶点和 3,856 个遍历者由NeptuneGraphQueryStep
,在剩余的处理过程中,这些数字保持不变,因为ProjectStep
和PathStep
正在格式化结果,而不是过滤它们。
与 TinkerPop 不同,Neptune 引擎不会通过以下方式优化性能膨胀其中NeptuneGraphQueryStep
和NeptuneTraverserConverterStep
步骤。膨胀是 TinkerPop 操作,它将遍历器组合在同一顶点上以减少运营开销,这就是导致Count
和Traversers
数字有所不同。由于膨胀只发生在 Neptune 委派给 TinkerPop 的步骤中,而不是在 Neptune 本身处理的步骤中发生,Count
和Traverser
列很少有不同。
“时间” 列报告了该步骤所花费的毫秒数,以及% Dur
列报告了该步骤所花费的总处理时间的百分比。这些指标通过显示花费时间最长的步骤来告诉你在哪里集中调整工作。
Neptune 中的指数运营指标profile
API 输出
Neptune 配置文件 API 输出中的另一组指标是索引操作:
Index Operations ================ Query execution: # of statement index ops: 23191 # of unique statement index ops: 5960 Duplication ratio: 3.89 # of terms materialized: 0
这些报告:
索引查找总数。
执行的唯一索引查找的数量。
总索引查找与唯一查找的比率。比率越低表示冗余减少。
从术语词典中实现的术语数。
在 Neptune 中重复指标profile
API 输出
如果你的穿越使用repeat()
步骤如上例所示,那么包含重复指标的部分将显示在profile
输出:
Repeat Metrics ============== Iteration Visited Output Until Emit Next ------------------------------------------------------ 0 2 0 0 0 2 1 53 0 0 0 53 2 3856 3856 3856 0 0 ------------------------------------------------------ 3911 3856 3856 0 55
这些报告:
一行的循环计数(
Iteration
column)。循环访问的元素数量(
Visited
column)。循环输出的元素数量(
Output
column)。循环输出的最后一个元素(
Until
column)。循环发出的元素数量(
Emit
column)。从循环传递到后续循环的元素数量(
Next
column)。
这些重复指标非常有助于理解遍历的分支因素,以便了解数据库正在完成多少工作。您可以使用这些数字来诊断性能问题,尤其是当同一遍历对不同参数的执行情况显著不同时。
Neptune 中的全文搜索指标profile
API 输出
当遍历使用全文搜索如上例所示,则包含全文搜索 (FTS) 指标的部分将显示在中。profile
输出:
FTS Metrics ============== SearchNode[(idVar=?1, query=Anchora~, field=city) . project ?1 .], {endpoint=your-OpenSearch-endpoint-URL, incomingSolutionsThreshold=1000, estimatedCardinality=INFINITY, remoteCallTimeSummary=[total=65, avg=32.500000, max=37, min=28], remoteCallTime=65, remoteCalls=2, joinTime=0, indexTime=0, remoteResults=2} 2 result(s) produced from SearchNode above
这显示了发送到 ElasticSearch (ES) 集群并报告有关与互动的几个指标 ElasticSearch 这可以帮助你查明与全文搜索有关的性能问题:
-
有关调用的摘要信息 ElasticSearch 索引:
所有 RemoteCall 满足查询所需的总毫秒数(
total
)。在 RemoteCall 上花费的平均毫秒数 (
avg
)。在 RemoteCall 中花费的最低毫秒数 (
min
)。在 RemoteCall 中花费的最大毫秒数 (
max
)。
RemoteCall 消耗的总时间 ElasticSearch (
remoteCallTime
)。拨打的 RemoteCass 的数量 ElasticSearch (
remoteCalls
)。在的连接中花费的毫秒数 ElasticSearch 结果 (
joinTime
)。索引查找所花费的毫秒数(
indexTime
)。返回的结果总数 ElasticSearch (
remoteResults
)。