Partilhar via


Como utilizar o passo do perfil de execução para avaliar as consultas do Gremlin

APLICA-SE A: Gremlin

Este artigo fornece uma visão geral de como usar a etapa de perfil de execução para bancos de dados gráficos do Azure Cosmos DB for Gremlin. Este passo fornece informações relevantes para a resolução de problemas e otimizações de consulta, e é compatível com qualquer consulta do Gremlin que possa ser executada relativamente a uma conta da API do Gremlin no Cosmos DB.

Para usar esta etapa, basta acrescentar a executionProfile() chamada de função no final da sua consulta Gremlin. Sua consulta Gremlin será executada e o resultado da operação retornará um objeto de resposta JSON com o perfil de execução da consulta.

Por exemplo:

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

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

Depois de chamar a executionProfile() etapa, a resposta será um objeto JSON que inclui a etapa Gremlin executada, o tempo total que levou e uma matriz dos operadores de tempo de execução do Cosmos DB em que a instrução resultou.

Nota

Esta implementação para Execution Profile não está definida na especificação Apache Tinkerpop. É específico do Azure Cosmos DB para a implementação do Gremlin.

Exemplo de resposta

A seguir está um exemplo anotado da saída que será retornada:

Nota

Este exemplo é anotado com comentários que explicam a estrutura geral da resposta. Uma resposta executionProfile real não conterá comentários.

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

Nota

A etapa executionProfile executará a consulta Gremlin. Isso inclui as addV etapas ou addE, que resultarão na criação e confirmarão as alterações especificadas na consulta. Como resultado, as Unidades de Solicitação geradas pela consulta Gremlin também serão cobradas.

Objetos de resposta de perfil de execução

A resposta de uma função executionProfile() produzirá uma hierarquia de objetos JSON com a seguinte estrutura:

  • Objeto da operação Gremlin: Representa toda a operação Gremlin que foi executada. Contém as seguintes propriedades.

    • gremlin: A declaração explícita de Gremlin que foi executada.
    • totalTime: O tempo, em milissegundos, em que a execução da etapa incorreu.
    • metrics: Uma matriz que contém cada um dos operadores de tempo de execução do Cosmos DB que foram executados para atender à consulta. Esta lista é ordenada por ordem de execução.
  • Operadores de tempo de execução do Cosmos DB: Representa cada um dos componentes de toda a operação Gremlin. Esta lista é ordenada por ordem de execução. Cada objeto contém as seguintes propriedades:

    • name: Nome do operador. Este é o tipo de etapa que foi avaliada e executada. Leia mais na tabela abaixo.
    • time: Quantidade de tempo, em milissegundos, que um determinado operador levou.
    • annotations: Contém informações adicionais, específicas para o operador que foi executado.
    • annotations.percentTime: Porcentagem do tempo total que levou para executar o operador específico.
    • counts: Número de objetos que foram retornados da camada de armazenamento por este operador. Isso está contido no counts.resultCount valor escalar dentro.
    • storeOps: Representa uma operação de armazenamento que pode abranger uma ou várias partições.
    • storeOps.fanoutFactor: Representa o número de partições que esta operação de armazenamento específica acessou.
    • storeOps.count: Representa o número de resultados retornados por esta operação de armazenamento.
    • storeOps.size: Representa o tamanho em bytes do resultado de uma determinada operação de armazenamento.
Operador de tempo de execução do Cosmos DB Gremlin Description
GetVertices Esta etapa obtém um conjunto predicado de objetos da camada de persistência.
GetEdges Esta etapa obtém as arestas adjacentes a um conjunto de vértices. Esta etapa pode resultar em uma ou várias operações de armazenamento.
GetNeighborVertices Esta etapa obtém os vértices que estão conectados a um conjunto de arestas. As bordas contêm as chaves de partição e ID's de seus vértices de origem e destino.
Coalesce Esta etapa é responsável pela avaliação de duas operações sempre que a coalesce() etapa Gremlin é executada.
CartesianProductOperator Esta etapa calcula um produto cartesiano entre dois conjuntos de dados. Geralmente executado sempre que os predicados to() ou from() são usados.
ConstantSourceOperator Esta etapa calcula uma expressão para produzir um valor constante como resultado.
ProjectOperator Esta etapa prepara e serializa uma resposta usando o resultado das operações anteriores.
ProjectAggregation Esta etapa prepara e serializa uma resposta para uma operação agregada.

Nota

Esta lista continuará a ser atualizada à medida que forem sendo adicionados novos operadores.

Exemplos de como analisar uma resposta de perfil de execução

A seguir estão exemplos de otimizações comuns que podem ser detetadas usando a resposta Perfil de Execução:

  • Consulta cega de fan-out.
  • Consulta não filtrada.

Padrões de consulta de distribuição cegos

Suponha a seguinte resposta de perfil de execução de um gráfico particionado:

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

Daí podem retirar-se as seguintes conclusões:

  • A consulta é uma única pesquisa de ID, uma vez que a instrução Gremlin segue o padrão g.V('id').
  • A julgar pela time métrica, a latência dessa consulta parece ser alta, já que é superior a 10ms para uma única operação de leitura pontual.
  • Se olharmos para o storeOps objeto, podemos ver que o é 5, o fanoutFactor que significa que 5 partições foram acessadas por esta operação.

Como conclusão desta análise, podemos determinar que a primeira consulta está acessando mais partições do que o necessário. Isso pode ser resolvido especificando a chave de particionamento na consulta como um predicado. Isso levará a menos latência e menos custo por consulta. Saiba mais sobre particionamento de gráficos. Uma consulta mais ideal seria g.V('tt0093640').has('partitionKey', 't1001').

Padrões de consulta não filtrados

Compare as duas respostas de perfil de execução a seguir. Para simplificar, esses exemplos usam um único gráfico particionado.

Esta primeira consulta recupera todos os vértices com o rótulo tweet e, em seguida, obtém seus vértices vizinhos:

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

Observe o perfil da mesma consulta, mas agora com um filtro adicional, has('lang', 'en')antes de explorar os vértices adjacentes:

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

Essas duas consultas alcançaram o mesmo resultado, no entanto, a primeira exigirá mais Unidades de Solicitação, pois precisava iterar um conjunto de dados inicial maior antes de consultar os itens adjacentes. Podemos ver indicadores deste comportamento quando comparamos os seguintes parâmetros de ambas as respostas:

  • O metrics[0].time valor é maior na primeira resposta, o que indica que essa única etapa demorou mais para ser resolvida.
  • O metrics[0].counts.resultsCount valor também é maior na primeira resposta, o que indica que o conjunto de dados de trabalho inicial era maior.

Próximos passos