Udostępnij za pośrednictwem


Jak użyć kroku profilu wykonywania do oceny zapytań aparatu Gremlin

DOTYCZY: Gremlin

Ten artykuł zawiera omówienie sposobu korzystania z kroku profilu wykonywania dla baz danych grafów języka Gremlin w usłudze Azure Cosmos DB. Ten krok zawiera istotne informacje dotyczące rozwiązywania problemów i optymalizacji zapytań oraz jest zgodny z dowolnymi zapytaniami Gremlin, które można wykonać względem konta interfejsu API Gremlin usługi Cosmos DB.

Aby użyć tego kroku, po prostu dołącz executionProfile() wywołanie funkcji na końcu zapytania Gremlin. Zapytanie Gremlin zostanie wykonane , a wynik operacji zwróci obiekt odpowiedzi JSON z profilem wykonywania zapytania.

Na przykład:

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

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

Po wywołaniu executionProfile() kroku odpowiedź będzie obiektem JSON zawierającym wykonany krok języka Gremlin, łączny czas trwania i tablicę operatorów środowiska uruchomieniowego usługi Cosmos DB, które spowodowały wykonanie instrukcji.

Uwaga

Ta implementacja profilu wykonywania nie jest zdefiniowana w specyfikacji apache Tinkerpop. Jest to specyficzne dla implementacji usługi Azure Cosmos DB dla języka Gremlin.

Przykład odpowiedzi

Poniżej znajduje się przykład z adnotacjami danych wyjściowych, które zostaną zwrócone:

Uwaga

Ten przykład jest oznaczony adnotacjami z komentarzami, które wyjaśniają ogólną strukturę odpowiedzi. Rzeczywista odpowiedź executionProfile nie będzie zawierać żadnych komentarzy.

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

Uwaga

Krok executionProfile wykona zapytanie Gremlin. Obejmuje addV to kroki lub addE, które spowodują utworzenie i zatwierdzą zmiany określone w zapytaniu. W związku z tym opłaty będą również naliczane za jednostki żądań wygenerowane przez zapytanie Gremlin.

Obiekty odpowiedzi profilu wykonywania

Odpowiedź funkcji executionProfile() zwróci hierarchię obiektów JSON o następującej strukturze:

  • Obiekt operacji języka Gremlin: reprezentuje całą wykonaną operację języka Gremlin. Zawiera następujące właściwości.

    • gremlin: jawna instrukcja języka Gremlin, która została wykonana.
    • totalTime: czas w milisekundach, w których wykonano krok.
    • metrics: Tablica zawierająca każdy operator środowiska uruchomieniowego usługi Cosmos DB, który został wykonany w celu spełnienia zapytania. Ta lista jest sortowana w kolejności wykonywania.
  • Operatory środowiska uruchomieniowego usługi Cosmos DB: reprezentuje poszczególne składniki całej operacji języka Gremlin. Ta lista jest sortowana w kolejności wykonywania. Każdy obiekt zawiera następujące właściwości:

    • name: nazwa operatora. Jest to typ kroku, który został oceniony i wykonany. Przeczytaj więcej w poniższej tabeli.
    • time: czas, w milisekundach, który wziął dany operator.
    • annotations: zawiera dodatkowe informacje specyficzne dla operatora, który został wykonany.
    • annotations.percentTime: procent całkowitego czasu wykonania określonego operatora.
    • counts: liczba obiektów zwróconych z warstwy magazynu przez ten operator. Jest to zawarte w wartości skalarnych w obrębie counts.resultCount .
    • storeOps: reprezentuje operację magazynu, która może obejmować jedną lub wiele partycji.
    • storeOps.fanoutFactor: reprezentuje liczbę partycji, do których uzyskiwano dostęp do tej konkretnej operacji magazynu.
    • storeOps.count: reprezentuje liczbę wyników zwróconych przez tę operację magazynu.
    • storeOps.size: reprezentuje rozmiar w bajtach wyniku danej operacji magazynu.
Operator środowiska uruchomieniowego Języka Gremlin w usłudze Cosmos DB opis
GetVertices Ten krok uzyskuje wstępnie określony zestaw obiektów z warstwy trwałości.
GetEdges Ten krok uzyskuje krawędzie sąsiadujące z zestawem wierzchołków. Ten krok może spowodować wykonanie jednej lub wielu operacji magazynowania.
GetNeighborVertices Ten krok uzyskuje wierzchołki połączone z zestawem krawędzi. Krawędzie zawierają klucze partycji i identyfikatory zarówno źródłowych, jak i docelowych wierzchołków.
Coalesce Ten krok obejmuje ocenę dwóch operacji przy każdym wykonaniu coalesce() kroku języka Gremlin.
CartesianProductOperator Ten krok oblicza produkt kartezjański między dwoma zestawami danych. Zwykle wykonywane za każdym razem, gdy predykaty to() lub from() są używane.
ConstantSourceOperator Ten krok oblicza wyrażenie w celu wygenerowania stałej wartości w wyniku.
ProjectOperator Ten krok przygotowuje i serializuje odpowiedź przy użyciu wyniku poprzednich operacji.
ProjectAggregation Ten krok przygotowuje i serializuje odpowiedź dla operacji agregującej.

Uwaga

Ta lista będzie nadal aktualizowana w miarę dodawania nowych operatorów.

Przykłady dotyczące analizowania odpowiedzi profilu wykonywania

Poniżej przedstawiono przykłady typowych optymalizacji, które można wykryć przy użyciu odpowiedzi profilu wykonywania:

  • Kwerenda ślepego wentylatora.
  • Niefiltrowane zapytanie.

Ślepe wzorce zapytań

Załóżmy, że następująca odpowiedź profilu wykonywania z partycjonowanego grafu:

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

Z tego można wyciągnąć następujące wnioski:

Podsumowując tę analizę, możemy określić, że pierwsze zapytanie uzyskuje dostęp do większej liczby partycji, niż jest to konieczne. Można to rozwiązać, określając klucz partycjonowania w zapytaniu jako predykat. Doprowadzi to do mniejszego opóźnienia i mniejszego kosztu na zapytanie. Dowiedz się więcej o partycjonowaniu grafu. Bardziej optymalne zapytanie to g.V('tt0093640').has('partitionKey', 't1001').

Niefiltrowane wzorce zapytań

Porównaj następujące dwie odpowiedzi profilu wykonywania. Dla uproszczenia w tych przykładach użyto pojedynczego partycjonowanego grafu.

To pierwsze zapytanie pobiera wszystkie wierzchołki z etykietą tweet , a następnie uzyskuje sąsiadujące wierzchołki:

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

Zwróć uwagę na profil tego samego zapytania, ale teraz z dodatkowym filtrem , has('lang', 'en')przed eksplorowanie sąsiednich wierzchołków:

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

Te dwa zapytania osiągnęły ten sam wynik, jednak pierwsza z nich będzie wymagać większej liczby jednostek żądania, ponieważ wymaga iteracji większego początkowego zestawu danych przed wykonaniem zapytania o sąsiadujące elementy. Możemy zobaczyć wskaźniki tego zachowania podczas porównywania następujących parametrów z obu odpowiedzi:

  • Wartość metrics[0].time jest wyższa w pierwszej odpowiedzi, co oznacza, że ten pojedynczy krok trwał dłużej.
  • Wartość metrics[0].counts.resultsCount jest wyższa, jak również w pierwszej odpowiedzi, co oznacza, że początkowy roboczy zestaw danych był większy.

Następne kroki