다음을 통해 공유

如何利用执行概要步骤来评估 Gremlin 查询

适用于: 小鬼

本文概述了如何使用 Azure Cosmos DB for Gremlin 图形数据库的执行概要步骤。 此步骤提供有关故障排除和查询优化的相关信息,并与可针对 Cosmos DB Gremlin API 帐户执行的任何 Gremlin 查询兼容。

若要使用此步骤,只需在 executionProfile() Gremlin 查询末尾追加函数调用。 将执行 Gremlin 查询 ,作的结果将返回包含查询执行配置文件的 JSON 响应对象。

例如:

    // Basic traversal
    g.V('mary').out()

    // Basic traversal with execution profile call
    g.V('mary').out().executionProfile()

调用 executionProfile() 该步骤后,响应将是一个 JSON 对象,其中包括执行的 Gremlin 步骤、所花费的总时间,以及语句生成的 Cosmos DB 运行时运算符的数组。

注释

Apache Tinkerpop 规范中不定义执行概况的实现。 它特定于用于 Gremlin 实现的 Azure Cosmos DB。

响应示例

下面是将返回的输出的批注示例:

注释

此示例使用注释来解释回复的一般结构。 实际的 executionProfile 响应不会包含任何注释。

[
  {
    // The Gremlin statement that was executed.
    "gremlin": "g.V('mary').out().executionProfile()",

    // Amount of time in milliseconds that the entire operation took.
    "totalTime": 28,

    // An array containing metrics for each of the steps that were executed. 
    // Each Gremlin step will translate to one or more of these steps.
    // This list is sorted in order of execution.
    "metrics": [
      {
        // This operation obtains a set of Vertex objects.
        // The metrics include: time, percentTime of total execution time, resultCount, 
        // fanoutFactor, count, size (in bytes) and time.
        "name": "GetVertices",
        "time": 24,
        "annotations": {
          "percentTime": 85.71
        },
        "counts": {
          "resultCount": 2
        },
        "storeOps": [
          {
            "fanoutFactor": 1,
            "count": 2,
            "size": 696,
            "time": 0.4
          }
        ]
      },
      {
        // This operation obtains a set of Edge objects. 
        // Depending on the query, these might be directly adjacent to a set of vertices, 
        // or separate, in the case of an E() query.
        //
        // The metrics include: time, percentTime of total execution time, resultCount, 
        // fanoutFactor, count, size (in bytes) and time.
        "name": "GetEdges",
        "time": 4,
        "annotations": {
          "percentTime": 14.29
        },
        "counts": {
          "resultCount": 1
        },
        "storeOps": [
          {
            "fanoutFactor": 1,
            "count": 1,
            "size": 419,
            "time": 0.67
          }
        ]
      },
      {
        // This operation obtains the vertices that a set of edges point at.
        // The metrics include: time, percentTime of total execution time and resultCount.
        "name": "GetNeighborVertices",
        "time": 0,
        "annotations": {
          "percentTime": 0
        },
        "counts": {
          "resultCount": 1
        }
      },
      {
        // This operation represents the serialization and preparation for a result from 
        // the preceding graph operations. The metrics include: time, percentTime of total 
        // execution time and resultCount.
        "name": "ProjectOperator",
        "time": 0,
        "annotations": {
          "percentTime": 0
        },
        "counts": {
          "resultCount": 1
        }
      }
    ]
  }
]

注释

executionProfile 步骤将执行 Gremlin 查询。 这包括addVaddE步骤,这将导致结果的创建,并提交查询中指定的更改。 因此,Gremlin 查询生成的请求单位也将被收费。

执行配置文件响应对象

executionProfile() 函数的响应将生成具有以下结构的 JSON 对象的层次结构:

  • Gremlin操作对象:表示已执行的整个 Gremlin操作。 包含以下属性。

    • gremlin:已执行的显式 Gremlin 语句。
    • totalTime:执行步骤所用时间(以毫秒为单位)。
    • metrics:一个数组,其中包含执行以完成查询的每个 Cosmos DB 运行时运算符。 此列表按执行顺序排序。
  • Cosmos DB 运行时运算符:表示整个 Gremlin 操作的每个组件。 此列表按执行顺序排序。 每个对象包含以下属性:

    • name:运算符的名称。 这是被评估和执行的那种步骤。 在下表中阅读详细信息。
    • time:给定运算符花费的时间(以毫秒为单位)。
    • annotations:包含特定于已执行的运算符的其他信息。
    • annotations.percentTime:执行特定运算符所花费的总时间的百分比。
    • counts:此运算符从存储层返回的对象数。 这在counts.resultCount标量值内包含。
    • storeOps:表示可以跨越一个或多个分区的存储操作。
    • storeOps.fanoutFactor:表示此特定存储操作访问的分区数。
    • storeOps.count:表示此存储作返回的结果数。
    • storeOps.size:表示给定存储作结果的大小(以字节为单位)。
Cosmos DB Gremlin 运行时运算符 Description
GetVertices 此步骤从持久性层获取一组谓词对象。
GetEdges 此步骤获取与一组顶点相邻的边缘。 此步骤可能会导致一个或多个存储操作。
GetNeighborVertices 此步骤获取连接到一组边缘的顶点。 边缘包含其源顶点和目标顶点的分区键和 ID。
Coalesce 此步骤用于在执行 coalesce() Gremlin 步骤时评估两个操作。
CartesianProductOperator 此步骤计算两个数据集之间的笛卡尔积。 通常在使用谓词to()from()时执行。
ConstantSourceOperator 此步骤计算表达式以生成常量值作为结果。
ProjectOperator 此步骤使用上述操作的结果来准备和序列化响应。
ProjectAggregation 此步骤准备并序列化集合操作的响应。

注释

随着新运算符的添加,此列表会继续更新。

有关如何分析执行剖面响应的示例

下面是可以使用执行配置文件响应发现的常见优化示例:

  • 盲人扇出查询。
  • 未筛选的查询。

盲扇出查询模式

假设以下执行配置文件响应来自分区的

[
  {
    "gremlin": "g.V('tt0093640').executionProfile()",
    "totalTime": 46,
    "metrics": [
      {
        "name": "GetVertices",
        "time": 46,
        "annotations": {
          "percentTime": 100
        },
        "counts": {
          "resultCount": 1
        },
        "storeOps": [
          {
            "fanoutFactor": 5,
            "count": 1,
            "size": 589,
            "time": 75.61
          }
        ]
      },
      {
        "name": "ProjectOperator",
        "time": 0,
        "annotations": {
          "percentTime": 0
        },
        "counts": {
          "resultCount": 1
        }
      }
    ]
  }
]

可以从中得出以下结论:

  • 查询是单个 ID 查找,因为 Gremlin 语句遵循模式 g.V('id')
  • time从指标来看,此查询的延迟似乎很高,因为单个点读操作的延迟超过 10 毫秒。
  • 如果查看对象 storeOps ,可以看到该 fanoutFactor 对象是 5,这意味着此作访问了 5 个分区

作为此分析的结论,我们可以确定第一个查询访问了超过必要的分区。 可以通过将查询中的分区键指定为谓词来解决此问题。 这会导致延迟减少,每个查询的成本也更少。 详细了解 图形分区。 更理想的查询是 g.V('tt0093640').has('partitionKey', 't1001')

未筛选的查询模式

比较以下两个执行概要结果。 为简单起见,这些示例使用单个分区图形。

此第一个查询使用标签 tweet 检索所有顶点,然后获取其相邻顶点:

[
  {
    "gremlin": "g.V().hasLabel('tweet').out().executionProfile()",
    "totalTime": 42,
    "metrics": [
      {
        "name": "GetVertices",
        "time": 31,
        "annotations": {
          "percentTime": 73.81
        },
        "counts": {
          "resultCount": 30
        },
        "storeOps": [
          {
            "fanoutFactor": 1,
            "count": 13,
            "size": 6819,
            "time": 1.02
          }
        ]
      },
      {
        "name": "GetEdges",
        "time": 6,
        "annotations": {
          "percentTime": 14.29
        },
        "counts": {
          "resultCount": 18
        },
        "storeOps": [
          {
            "fanoutFactor": 1,
            "count": 20,
            "size": 7950,
            "time": 1.98
          }
        ]
      },
      {
        "name": "GetNeighborVertices",
        "time": 5,
        "annotations": {
          "percentTime": 11.9
        },
        "counts": {
          "resultCount": 20
        },
        "storeOps": [
          {
            "fanoutFactor": 1,
            "count": 4,
            "size": 1070,
            "time": 1.19
          }
        ]
      },
      {
        "name": "ProjectOperator",
        "time": 0,
        "annotations": {
          "percentTime": 0
        },
        "counts": {
          "resultCount": 20
        }
      }
    ]
  }
]

请注意同一查询的性能分析,但现在有一个附加筛选器 has('lang', 'en'),再浏览相邻顶点:

[
  {
    "gremlin": "g.V().hasLabel('tweet').has('lang', 'en').out().executionProfile()",
    "totalTime": 14,
    "metrics": [
      {
        "name": "GetVertices",
        "time": 14,
        "annotations": {
          "percentTime": 58.33
        },
        "counts": {
          "resultCount": 11
        },
        "storeOps": [
          {
            "fanoutFactor": 1,
            "count": 11,
            "size": 4807,
            "time": 1.27
          }
        ]
      },
      {
        "name": "GetEdges",
        "time": 5,
        "annotations": {
          "percentTime": 20.83
        },
        "counts": {
          "resultCount": 18
        },
        "storeOps": [
          {
            "fanoutFactor": 1,
            "count": 18,
            "size": 7159,
            "time": 1.7
          }
        ]
      },
      {
        "name": "GetNeighborVertices",
        "time": 5,
        "annotations": {
          "percentTime": 20.83
        },
        "counts": {
          "resultCount": 18
        },
        "storeOps": [
          {
            "fanoutFactor": 1,
            "count": 4,
            "size": 1070,
            "time": 1.01
          }
        ]
      },
      {
        "name": "ProjectOperator",
        "time": 0,
        "annotations": {
          "percentTime": 0
        },
        "counts": {
          "resultCount": 18
        }
      }
    ]
  }
]

但是,这两个查询达到了相同的结果,但第一个查询需要更多请求单位,因为它需要在查询相邻项之前循环访问更大的初始数据集。 在比较两个响应中的以下参数时,我们可以看到此行为的指示器:

  • 在第metrics[0].time个响应中,数值较高,这表示这一步骤耗费了更长的时间才能完成。
  • 该值 metrics[0].counts.resultsCount 在第一个响应中也更高,指示初始工作数据集更大。

后续步骤