実行プロファイル ステップを使用して Gremlin のクエリを評価する方法How to use the execution profile step to evaluate your Gremlin queries

この記事では、Azure Cosmos DB Gremlin API グラフ データベースの実行プロファイル ステップを使用する方法の概要を示します。This article provides an overview of how to use the execution profile step for Azure Cosmos DB Gremlin API graph databases. このステップでは、トラブルシューティングとクエリの最適化に関連する情報が提供され、Cosmos DB Gremlin API アカウントに対して実行できるすべての Gremlin クエリと互換性があります。This step provides relevant information for troubleshooting and query optimizations, and it is compatible with any Gremlin query that can be executed against a Cosmos DB Gremlin API account.

このステップを使用するには、Gremlin クエリの最後に executionProfile() 関数の呼び出しを追加するだけです。To use this step, simply append the executionProfile() function call at the end of your Gremlin query. Gremlin クエリが実行され、操作の結果でクエリ実行プロファイルを含む JSON 応答オブジェクトが返されます。Your Gremlin query will be executed and the result of the operation will return a JSON response object with the query execution profile.

例:For example:

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

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

executionProfile() ステップを呼び出した後の応答は、実行された Gremlin ステップ、かかった合計時間、ステートメントの結果の Cosmos DB ランタイム演算子の配列が含まれる、JSON オブジェクトになります。After calling the executionProfile() step, the response will be a JSON object that includes the executed Gremlin step, the total time it took, and an array of the Cosmos DB runtime operators that the statement resulted in.

注意

実行プロファイルのこの実装は、Apache Tinkerpop の仕様では定義されていません。This implementation for Execution Profile is not defined in the Apache Tinkerpop specification. それは、Azure Cosmos DB Gremlin API の実装に固有です。It is specific to Azure Cosmos DB Gremlin API's implementation.

応答の例Response Example

以下に示すのは、返される出力の注釈付きの例です。The following is an annotated example of the output that will be returned:

注意

この例には、応答の一般的な構造を説明するコメントで注釈が付けられています。This example is annotated with comments that explain the general structure of the response. 実際の executionProfile の応答には、コメントは含まれません。An actual executionProfile response won't contain any comments.

[
  {
    // 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 クエリが実行されます。The executionProfile step will execute the Gremlin query. これには addV または addE ステップが含まれ、これらでは、作成が行われ、クエリで指定されている変更がコミットされます。This includes the addV or addEsteps, which will result in the creation and will commit the changes specified in the query. その結果、Gremlin クエリによって生成される要求ユニットの課金も行われます。As a result, the Request Units generated by the Gremlin query will also be charged.

実行プロファイルの応答オブジェクトExecution profile response objects

executionProfile() 関数の応答では、次の構造を持つ JSON オブジェクトの階層が生成されます。The response of an executionProfile() function will yield a hierarchy of JSON objects with the following structure:

  • Gremlin 操作オブジェクト: 実行された Gremlin 操作全体を表します。Gremlin operation object: Represents the entire Gremlin operation that was executed. 次のプロパティが含まれます。Contains the following properties.

    • gremlin:実行された明示的な Gremlin ステートメント。gremlin: The explicit Gremlin statement that was executed.
    • totalTime:ステップの実行にかかった時間 (ミリ秒単位)。totalTime: The time, in milliseconds, that the execution of the step incurred in.
    • metrics:クエリを満たすために実行された各 Cosmos DB ランタイム演算子が含まれる配列。metrics: An array that contains each of the Cosmos DB runtime operators that were executed to fulfill the query. このリストは、実行の順序で並べられています。This list is sorted in order of execution.
  • Cosmos DB ランタイム演算子: Gremlin 操作全体の各コンポーネントを表します。Cosmos DB runtime operators: Represents each of the components of the entire Gremlin operation. このリストは、実行の順序で並べられています。This list is sorted in order of execution. 各オブジェクトには次のプロパティが含まれます。Each object contains the following properties:

    • name:演算子の名前。name: Name of the operator. これは、評価および実行されたステップの種類です。This is the type of step that was evaluated and executed. 詳しくは、後の表をご覧ください。Read more in the table below.
    • time:特定の演算子にかかった時間 (ミリ秒単位)。time: Amount of time, in milliseconds, that a given operator took.
    • annotations:実行された演算子に固有の追加情報が含まれます。annotations: Contains additional information, specific to the operator that was executed.
    • annotations.percentTime:特定の演算子の実行にかかった合計時間の割合。annotations.percentTime: Percentage of the total time that it took to execute the specific operator.
    • counts:この演算子によって、ストレージ レイヤーから返されたオブジェクトの数。counts: Number of objects that were returned from the storage layer by this operator. これは、counts.resultCount スカラー値に含まれます。This is contained in the counts.resultCount scalar value within.
    • storeOps:1 つまたは複数のパーティションにまたがることができるストレージ操作を表します。storeOps: Represents a storage operation that can span one or multiple partitions.
    • storeOps.fanoutFactor:この特定のストレージ操作でアクセスされたパーティションの数を表します。storeOps.fanoutFactor: Represents the number of partitions that this specific storage operation accessed.
    • storeOps.count:このストレージ操作で返された結果の数を表します。storeOps.count: Represents the number of results that this storage operation returned.
    • storeOps.size:特定のストレージ操作の結果のサイズを表します (バイト単位)。storeOps.size: Represents the size in bytes of the result of a given storage operation.
Cosmos DB Gremlin ランタイム演算子Cosmos DB Gremlin Runtime Operator 説明Description
GetVertices このステップでは、永続化レイヤーから基にされたオブジェクトのセットが取得されます。This step obtains a predicated set of objects from the persistence layer.
GetEdges このステップでは、頂点のセットに隣接するエッジが取得されます。This step obtains the edges that are adjacent to a set of vertices. このステップは、1 つまたは複数のストレージ操作になる可能性があります。This step can result in one or many storage operations.
GetNeighborVertices このステップでは、エッジのセットに接続された頂点が取得されます。This step obtains the vertices that are connected to a set of edges. エッジには、ソース頂点とターゲット頂点両方のパーティション キーと ID が含まれます。The edges contain the partition keys and ID's of both their source and target vertices.
Coalesce このステップでは、coalesce() Gremlin ステップが実行されるときは常に、2 つの操作の評価が考慮されます。This step accounts for the evaluation of two operations whenever the coalesce() Gremlin step is executed.
CartesianProductOperator このステップでは、2 つのデータセット間のデカルト積が計算されます。This step computes a cartesian product between two datasets. 通常、述語 to() または from() が使用されると常に実行されます。Usually executed whenever the predicates to() or from() are used.
ConstantSourceOperator このステップでは、式が計算され、結果として定数値が生成されます。This step computes an expression to produce a constant value as a result.
ProjectOperator このステップでは、それ以前の操作の結果を使用して、応答が準備されてシリアル化されます。This step prepares and serializes a response using the result of preceding operations.
ProjectAggregation このステップでは、集計操作の応答が準備されてシリアル化されます。This step prepares and serializes a response for an aggregate operation.

注意

この一覧は、新しい演算子が追加されると更新されます。This list will continue to be updated as new operators are added.

実行プロファイルの応答を分析する方法の例Examples on how to analyze an execution profile response

実行プロファイルの応答を使用して特定できる一般的な最適化の例を次に示します。The following are examples of common optimizations that can be spotted using the Execution Profile response:

  • ブラインド ファンアウト クエリ。Blind fan-out query.
  • フィルター処理なしクエリ。Unfiltered query.

ブラインド ファンアウト クエリのパターンBlind fan-out query patterns

パーティション分割されたグラフからの次のような実行プロファイルの応答を想定します。Assume the following execution profile response from a partitioned graph:

[
  {
    "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
        }
      }
    ]
  }
]

次のような結論が得られます。The following conclusions can be made from it:

  • Gremlin ステートメントはパターン g.V('id') に従っているため、クエリは単一 ID 参照です。The query is a single ID lookup, since the Gremlin statement follows the pattern g.V('id').
  • time メトリックから判断して、1 つのポイント読み取り操作が 10 ミリ秒より長いので、このクエリの待機時間は長いと思われます。Judging from the time metric, the latency of this query seems to be high since it's more than 10ms for a single point-read operation.
  • storeOps オブジェクトを見ると、fanoutFactor5 であることを確認でき、これは 5 つのパーティションがこの操作によってアクセスされたことを意味します。If we look into the storeOps object, we can see that the fanoutFactor is 5, which means that 5 partitions were accessed by this operation.

この分析の結論として、最初のクエリが必要以上に多くのパーティションにアクセスしていると判断できます。As a conclusion of this analysis, we can determine that the first query is accessing more partitions than necessary. これは、述語としてクエリでパーティション キーを指定することにより解決できます。This can be addressed by specifying the partitioning key in the query as a predicate. これにより、待機時間が短縮され、クエリあたりのコストが減ります。This will lead to less latency and less cost per query. 詳しくは、グラフのパーティション分割に関する記事をご覧ください。Learn more about graph partitioning. より最適なクエリは、g.V('tt0093640').has('partitionKey', 't1001') です。A more optimal query would be g.V('tt0093640').has('partitionKey', 't1001').

フィルター処理なしクエリのパターンUnfiltered query patterns

次の 2 つの実行プロファイルの応答を比較します。Compare the following two execution profile responses. わかりやすくするため、これらの例では 1 つのパーティション分割されたグラフを使用しています。For simplicity, these examples use a single partitioned graph.

この最初のクエリでは、ラベル tweet の付いたすべての頂点が取得された後、隣接する頂点が取得されます。This first query retrieves all vertices with the label tweet and then obtains their neighboring vertices:

[
  {
    "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') が追加されている場合のプロファイルに注意してください。Notice the profile of the same query, but now with an additional filter, has('lang', 'en'), before exploring the adjacent vertices:

[
  {
    "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
        }
      }
    ]
  }
]

これら 2 つのクエリは同じ結果になりますが、1 つ目のクエリでは、隣接する項目のクエリを実行する前に、大規模な初期データセットを反復処理する必要があるため、より多くの要求ユニットが必要になります。These two queries reached the same result, however, the first one will require more Request Units since it needed to iterate a larger initial dataset before querying the adjacent items. 両方の応答から次のパラメーターを比較すると、この動作のインジケーターを確認できます。We can see indicators of this behavior when comparing the following parameters from both responses:

  • 最初の応答の方が metrics[0].time の値が高く、これはこの 1 つのステップの解決に長くかかったことを示します。The metrics[0].time value is higher in the first response, which indicates that this single step took longer to resolve.
  • metrics[0].counts.resultsCount の値も最初の応答の方が高く、初期作業データセットが大きかったことを示します。The metrics[0].counts.resultsCount value is higher as well in the first response, which indicates that the initial working dataset was larger.

次の手順Next steps