Använda stegen i körningsprofilen för att utvärdera Gremlin-frågor

GÄLLER FÖR: Gremlin

Den här artikeln innehåller en översikt över hur du använder körningsprofilsteget för Azure Cosmos DB för Gremlin-grafdatabaser. Det här steget ger relevant information för felsökning och frågekörning, och den är kompatibel med alla Gremlin-frågor som kan utföras mot ett Cosmos DB Gremlin API-konto.

Om du vill använda det här steget lägger du helt enkelt till funktionsanropet executionProfile() i slutet av Gremlin-frågan. Gremlin-frågan körs och resultatet av åtgärden returnerar ett JSON-svarsobjekt med frågekörningsprofilen.

Exempel:

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

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

När du har anropat executionProfile() steget blir svaret ett JSON-objekt som innehåller det utförda Gremlin-steget, den totala tid det tog och en matris med Cosmos DB-körningsoperatorerna som -instruktionen resulterade i.

Anteckning

Den här implementeringen för körningsprofilen definieras inte i Apache Tinkerpop-specifikationen. Den är specifik för Implementeringen av Azure Cosmos DB för Gremlin.

Svarsexempel

Följande är ett kommenterat exempel på utdata som ska returneras:

Anteckning

Det här exemplet kommenteras med kommentarer som förklarar svarets allmänna struktur. Ett faktiskt executionProfile-svar innehåller inga kommentarer.

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

Anteckning

Steget executionProfile kör Gremlin-frågan. Detta inkluderar addV stegen eller addEsom resulterar i skapandet och checkar in ändringarna som anges i frågan. Därför debiteras även de enheter för programbegäran som genereras av Gremlin-frågan.

Svarsobjekt för körningsprofil

Svaret från en executionProfile()-funktion ger en hierarki med JSON-objekt med följande struktur:

  • Gremlin-åtgärdsobjekt: Representerar hela Gremlin-åtgärden som kördes. Innehåller följande egenskaper.

    • gremlin: Den explicita Gremlin-instruktionen som kördes.
    • totalTime: Den tid i millisekunder som körningen av steget uppstod i.
    • metrics: En matris som innehåller var och en av Cosmos DB-körningsoperatorerna som kördes för att uppfylla frågan. Den här listan sorteras i körningsordning.
  • Cosmos DB-körningsoperatorer: Representerar var och en av komponenterna i hela Gremlin-åtgärden. Den här listan sorteras i körningsordning. Varje objekt innehåller följande egenskaper:

    • name: Namnet på operatorn. Det här är den typ av steg som utvärderades och kördes. Läs mer i tabellen nedan.
    • time: Den tid i millisekunder som en viss operator tog.
    • annotations: Innehåller ytterligare information som är specifik för operatorn som kördes.
    • annotations.percentTime: Procentandel av den totala tid det tog att köra den specifika operatorn.
    • counts: Antalet objekt som returnerades från lagringsskiktet av den här operatorn. Detta finns i det counts.resultCount skalära värdet i .
    • storeOps: Representerar en lagringsåtgärd som kan sträcka sig över en eller flera partitioner.
    • storeOps.fanoutFactor: Representerar antalet partitioner som den här specifika lagringsåtgärden har åtkomst till.
    • storeOps.count: Representerar antalet resultat som den här lagringsåtgärden returnerade.
    • storeOps.size: Representerar storleken i byte för resultatet av en viss lagringsåtgärd.
Cosmos DB Gremlin Runtime-operatör Beskrivning
GetVertices Det här steget hämtar en predikatuppsättning med objekt från beständighetsskiktet.
GetEdges Det här steget hämtar kanterna som ligger intill en uppsättning hörn. Det här steget kan resultera i en eller flera lagringsåtgärder.
GetNeighborVertices Det här steget hämtar hörnen som är anslutna till en uppsättning kanter. Kanterna innehåller partitionsnycklarna och ID:na för både käll- och målhörn.
Coalesce Det här steget redovisar utvärderingen av två åtgärder när coalesce() Gremlin-steget körs.
CartesianProductOperator Det här steget beräknar en kartesisk produkt mellan två datauppsättningar. Körs vanligtvis när predikaten to() eller from() används.
ConstantSourceOperator Det här steget beräknar ett uttryck för att skapa ett konstant värde som ett resultat.
ProjectOperator Det här steget förbereder och serialiserar ett svar med hjälp av resultatet från föregående åtgärder.
ProjectAggregation Det här steget förbereder och serialiserar ett svar för en aggregeringsåtgärd.

Anteckning

Den här listan fortsätter att uppdateras när nya operatorer läggs till.

Exempel på hur du analyserar svar från en körningsprofil

Följande är exempel på vanliga optimeringar som kan upptäckas med hjälp av körningsprofilsvaret:

  • Blind fan-out fråga.
  • Ofiltrerad fråga.

Frågemönster för blind utsöndrad

Anta följande svar för körningsprofilen från en partitionerad graf:

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

Följande slutsatser kan dras av den:

  • Frågan är en enda ID-sökning eftersom Gremlin-instruktionen följer mönstret g.V('id').
  • Att döma av måttet time verkar svarstiden för den här frågan vara hög eftersom den är mer än 10 ms för en enda punktläsningsåtgärd.
  • Om vi tittar på - storeOps objektet kan vi se att fanoutFactor är 5, vilket innebär att 5 partitioner användes av den här åtgärden.

Som en slutsats av den här analysen kan vi fastställa att den första frågan har åtkomst till fler partitioner än nödvändigt. Detta kan åtgärdas genom att ange partitioneringsnyckeln i frågan som ett predikat. Detta leder till mindre svarstid och mindre kostnad per fråga. Läs mer om grafpartitionering. En mer optimal fråga är g.V('tt0093640').has('partitionKey', 't1001').

Ofiltrerade frågemönster

Jämför följande två svar för körningsprofilen. För enkelhetens skull använder de här exemplen en enda partitionerad graf.

Den första frågan hämtar alla hörn med etiketten tweet och hämtar sedan grannhörn:

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

Observera profilen för samma fråga, men nu med ytterligare ett filter, has('lang', 'en'), innan du utforskar de intilliggande hörnen:

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

Dessa två frågor nådde samma resultat, men den första kräver fler enheter för programbegäran eftersom den behövde iterera en större inledande datauppsättning innan du frågar de intilliggande objekten. Vi kan se indikatorer på det här beteendet när du jämför följande parametrar från båda svaren:

  • Värdet metrics[0].time är högre i det första svaret, vilket indikerar att det här enskilda steget tog längre tid att lösa.
  • Värdet metrics[0].counts.resultsCount är också högre i det första svaret, vilket indikerar att den första arbetsdatauppsättningen var större.

Nästa steg