Share via


Metrische gegevens over de uitvoering van SQL-query's ophalen en queryprestaties analyseren met behulp van .NET SDK

VAN TOEPASSING OP: NoSQL

In dit artikel wordt beschreven hoe u de prestaties van SQL-query's in Azure Cosmos DB profileert met behulp van ServerSideCumulativeMetrics die zijn opgehaald uit de .NET SDK. ServerSideCumulativeMetrics is een sterk getypt object met informatie over de uitvoering van de back-endquery. Het bevat cumulatieve metrische gegevens die worden geaggregeerd over alle fysieke partities voor de aanvraag, een lijst met metrische gegevens voor elke fysieke partitie en de totale aanvraagkosten. Deze metrische gegevens worden uitgebreid beschreven in het artikel Queryprestaties afstemmen.

Metrische gegevens van de query ophalen

Metrische querygegevens zijn beschikbaar als een sterk getypt object in de .NET SDK vanaf versie 3.36.0. Voorafgaand aan deze versie of als u een andere SDK-taal gebruikt, kunt u metrische querygegevens ophalen door het Diagnosticste parseren. In het volgende codevoorbeeld ziet u hoe u deze kunt ophalen ServerSideCumulativeMetrics uit de Diagnosticsfeedresponse:

CosmosClient client = new CosmosClient(myCosmosEndpoint, myCosmosKey);
Container container = client.GetDatabase(myDatabaseName).GetContainer(myContainerName);

QueryDefinition query = new QueryDefinition("SELECT TOP 5 * FROM c");
FeedIterator<MyClass> feedIterator = container.GetItemQueryIterator<MyClass>(query);

while (feedIterator.HasMoreResults)
{
    // Execute one continuation of the query
    FeedResponse<MyClass> feedResponse = await feedIterator.ReadNextAsync();

    // Retrieve the ServerSideCumulativeMetrics object from the FeedResponse
    ServerSideCumulativeMetrics metrics = feedResponse.Diagnostics.GetQueryMetrics();
}

U kunt ook metrische querygegevens ophalen uit een FeedResponse LINQ-query met behulp van de ToFeedIterator() methode:

FeedIterator<MyClass> feedIterator = container.GetItemLinqQueryable<MyClass>()
    .Take(5)
    .ToFeedIterator();

while (feedIterator.HasMoreResults)
{
    FeedResponse<MyClass> feedResponse = await feedIterator.ReadNextAsync();
    ServerSideCumulativeMetrics metrics = feedResponse.Diagnostics.GetQueryMetrics();
}

Cumulatieve metrische gegevens

ServerSideCumulativeMetrics bevat een CumulativeMetrics eigenschap die de metrische querygegevens vertegenwoordigt die zijn geaggregeerd over alle partities voor de enkele retour.

// Retrieve the ServerSideCumulativeMetrics object from the FeedResponse
ServerSideCumulativeMetrics metrics = feedResponse.Diagnostics.GetQueryMetrics();

// CumulativeMetrics is the metrics for this continuation aggregated over all partitions
ServerSideMetrics cumulativeMetrics = metrics.CumulativeMetrics;

U kunt deze metrische gegevens ook aggregeren voor alle retouren voor de query. Hier volgt een voorbeeld van het aggregeren van de uitvoeringstijd van query's voor alle retouren voor een bepaalde query met behulp van LINQ:

QueryDefinition query = new QueryDefinition("SELECT TOP 5 * FROM c");
FeedIterator<MyClass> feedIterator = container.GetItemQueryIterator<MyClass>(query);

List<ServerSideCumulativeMetrics> metrics = new List<ServerSideCumulativeMetrics>();
TimeSpan cumulativeTime;
while (feedIterator.HasMoreResults)
{
    // Execute one continuation of the query
    FeedResponse<MyClass> feedResponse = await feedIterator.ReadNextAsync();

    // Store the ServerSideCumulativeMetrics object to aggregate values after all round trips
    metrics.Add(response.Diagnostics.GetQueryMetrics());
}

// Aggregate values across trips for metrics of interest
TimeSpan totalTripsExecutionTime = metrics.Aggregate(TimeSpan.Zero, (currentSum, next) => currentSum + next.CumulativeMetrics.TotalTime);
DoSomeLogging(totalTripsExecutionTime);

Gepartitioneerde metrische gegevens

ServerSideCumulativeMetrics bevat een PartitionedMetrics eigenschap die een lijst is met metrische gegevens per partitie voor de retour. Als meerdere fysieke partities in één retour worden bereikt, worden de metrische gegevens voor elk van deze partities weergegeven in de lijst. Gepartitioneerde metrische gegevens worden weergegeven als ServerSidePartitionedMetrics met een unieke id voor elke fysieke partitie en aanvraagkosten voor die partitie.

// Retrieve the ServerSideCumulativeMetrics object from the FeedResponse
ServerSideCumulativeMetrics metrics = feedResponse.Diagnostics.GetQueryMetrics();

// PartitionedMetrics is a list of per-partition metrics for this continuation
List<ServerSidePartitionedMetrics> partitionedMetrics = metrics.PartitionedMetrics;

Wanneer u alle retouren hebt verzameld, kunt u metrische gegevens per partitie zien of een specifieke partitie prestatieproblemen veroorzaakt in vergelijking met anderen. Hier volgt een voorbeeld van het groeperen van metrische gegevens voor elke reis met behulp van LINQ:

QueryDefinition query = new QueryDefinition("SELECT TOP 5 * FROM c");
FeedIterator<MyClass> feedIterator = container.GetItemQueryIterator<MyClass>(query);

List<ServerSideCumulativeMetrics> metrics = new List<ServerSideCumulativeMetrics>();
while (feedIterator.HasMoreResults)
{
    // Execute one continuation of the query
    FeedResponse<MyClass> feedResponse = await feedIterator.ReadNextAsync();

    // Store the ServerSideCumulativeMetrics object to aggregate values after all round trips
    metrics.Add(response.Diagnostics.GetQueryMetrics());
}

// Group metrics by partition key range id
var groupedPartitionMetrics = metrics.SelectMany(m => m.PartitionedMetrics).GroupBy(p => p.PartitionKeyRangeId);
foreach(var partitionGroup in groupedPartitionMetrics)
{
    foreach(var tripMetrics in partitionGroup)
    {
        DoSomethingWithMetrics();
    }
}

De kosten voor de queryaanvraag ophalen

U kunt de aanvraageenheden vastleggen die door elke query worden gebruikt om dure query's of query's te onderzoeken die hoge doorvoer verbruiken. U kunt de totale aanvraagkosten ophalen met behulp van de TotalRequestCharge eigenschap in ServerSideCumulativeMetrics of u kunt de aanvraagkosten van elke partitie bekijken met behulp van de RequestCharge eigenschap voor elke ServerSidePartitionedMetrics geretourneerde partitie.

De totale aanvraagkosten zijn ook beschikbaar met behulp van de RequestCharge eigenschap in FeedResponse. Zie het artikel over kosten voor aanvraageenheden voor meer informatie over het verkrijgen van de aanvraagkosten met behulp van Azure Portal en verschillende SDK's.

QueryDefinition query = new QueryDefinition("SELECT TOP 5 * FROM c");
FeedIterator<MyClass> feedIterator = container.GetItemQueryIterator<MyClass>(query);

while (feedIterator.HasMoreResults)
{
    // Execute one continuation of the query
    FeedResponse<MyClass> feedResponse = await feedIterator.ReadNextAsync();
    double requestCharge = feedResponse.RequestCharge;

    // Log the RequestCharge how ever you want.
    DoSomeLogging(requestCharge);
}

De uitvoeringstijd van de query ophalen

U kunt de uitvoeringstijd van query's vastleggen voor elke reis vanuit de metrische querygegevens. Wanneer u de latentie van aanvragen bekijkt, is het belangrijk om de uitvoeringstijd van query's te onderscheiden van andere latentiebronnen, zoals de doorvoertijd van het netwerk. In het volgende voorbeeld ziet u hoe u de cumulatieve uitvoeringstijd van query's voor elke retour kunt ophalen:

QueryDefinition query = new QueryDefinition("SELECT TOP 5 * FROM c");
FeedIterator<MyClass> feedIterator = container.GetItemQueryIterator<MyClass>(query);

TimeSpan cumulativeTime;
while (feedIterator.HasMoreResults)
{
    // Execute one continuation of the query
    FeedResponse<MyClass> feedResponse = await feedIterator.ReadNextAsync();
    ServerSideCumulativeMetrics metrics = response.Diagnostics.GetQueryMetrics();
    cumulativeTime = metrics.CumulativeMetrics.TotalTime;
}

// Log the elapsed time
DoSomeLogging(cumulativeTime);

Het indexgebruik ophalen

Als u het indexgebruik bekijkt, kunt u problemen met trage query's opsporen. Query's die het indexresultaat niet kunnen gebruiken, worden uitgevoerd in een volledige scan van alle documenten in een container voordat de resultatenset wordt geretourneerd.

Hier volgt een voorbeeld van een scanquery:

SELECT VALUE c.description 
FROM   c 
WHERE UPPER(c.description) = "BABYFOOD, DESSERT, FRUIT DESSERT, WITHOUT ASCORBIC ACID, JUNIOR"

Het filter van deze query maakt gebruik van de systeemfunctie UPPER, die niet wordt geleverd vanuit de index. Als u deze query uitvoert op een grote verzameling, zijn de volgende metrische querygegevens geproduceerd voor de eerste voortzetting:

QueryMetrics

Retrieved Document Count                 :          60,951
Retrieved Document Size                  :     399,998,938 bytes
Output Document Count                    :               7
Output Document Size                     :             510 bytes
Index Utilization                        :            0.00 %
Total Query Execution Time               :        4,500.34 milliseconds
Query Preparation Time                   :             0.2 milliseconds
Index Lookup Time                        :            0.01 milliseconds
Document Load Time                       :        4,177.66 milliseconds
Runtime Execution Time                   :           407.9 milliseconds
Document Write Time                      :            0.01 milliseconds

Let op de volgende waarden uit de uitvoer van de metrische querygegevens:

Retrieved Document Count                 :          60,951
Retrieved Document Size                  :     399,998,938 bytes

Deze query heeft 60.951 documenten geladen, met een totaal van 399.998.938 bytes. Het laden van dit aantal bytes resulteert in hoge kosten of kosten voor aanvraageenheden. Het duurt ook lang om de query uit te voeren, wat duidelijk is met de eigenschap totale tijd die is besteed:

Total Query Execution Time               :        4,500.34 milliseconds

Dit betekent dat de query 4,5 seconden duurde om uit te voeren (en dit was slechts één vervolg).

Als u deze voorbeeldquery wilt optimaliseren, vermijdt u het gebruik van UPPER in het filter. Wanneer documenten worden gemaakt of bijgewerkt, moeten de c.description waarden in alle hoofdletters worden ingevoegd. De query wordt vervolgens:

SELECT VALUE c.description 
FROM   c 
WHERE c.description = "BABYFOOD, DESSERT, FRUIT DESSERT, WITHOUT ASCORBIC ACID, JUNIOR"

Deze query kan nu worden geleverd vanuit de index. U kunt ook berekende eigenschappen gebruiken om de resultaten van systeemfuncties of complexe berekeningen te indexeren die anders tot een volledige scan zouden leiden.

Zie het artikel Queryprestaties afstemmen voor meer informatie over het afstemmen van queryprestaties .

Verwijzingen

Volgende stappen