Hämta SQL-frågekörningsmått och analysera frågeprestanda med hjälp av .NET SDK

GÄLLER FÖR: NoSQL

Den här artikeln beskriver hur du profilerar SQL-frågeprestanda i Azure Cosmos DB med hjälp av ServerSideCumulativeMetrics som hämtats från .NET SDK. ServerSideCumulativeMetrics är ett starkt skrivet objekt med information om körningen av serverdelsfrågan. Den innehåller kumulativa mått som aggregeras över alla fysiska partitioner för begäran, en lista med mått för varje fysisk partition och den totala kostnaden för begäran. Dessa mått dokumenteras mer detaljerat i artikeln Tune Query Performance (Finjustera frågeprestanda ).

Hämta frågans mätvärden

Frågemått är tillgängliga som ett starkt skrivet objekt i .NET SDK med början i version 3.36.0. Innan den här versionen, eller om du använder ett annat SDK-språk, kan du hämta frågemått genom att parsa Diagnostics. Följande kodexempel visar hur du hämtar ServerSideCumulativeMetrics från i en FeedResponseDiagnostics:

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();
}

Du kan också hämta frågemått från FeedResponse en LINQ-fråga med hjälp av ToFeedIterator() metoden:

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

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

Kumulativa mått

ServerSideCumulativeMetrics innehåller en CumulativeMetrics egenskap som representerar frågemåtten aggregerade över alla partitioner för enkel tur och retur.

// 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;

Du kan också aggregera dessa mått över alla rundturer för frågan. Följande är ett exempel på hur du aggregerar körningstiden för frågor över alla tur och retur-turer för en viss fråga med 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(feedResponse.Diagnostics.GetQueryMetrics());
}

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

Partitionerade mått

ServerSideCumulativeMetrics innehåller en PartitionedMetrics egenskap som är en lista över mått per partition för tur och retur. Om flera fysiska partitioner nås under en enda tur och retur visas mått för var och en av dem i listan. Partitionerade mått representeras som ServerSidePartitionedMetrics med en unik identifierare för varje fysisk partition och begärandeavgift för den partitionen.

// 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;

När de ackumuleras under alla turer per partition kan du se om en specifik partition orsakar prestandaproblem jämfört med andra. Följande är ett exempel på hur du grupperar partitionsmått för varje resa med 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(feedResponse.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();
    }
}

Hämta frågebegärandeavgiften

Du kan samla in de enheter för begäranden som används av varje fråga för att undersöka dyra frågor eller frågor som förbrukar högt dataflöde. Du kan hämta den totala begärandeavgiften med hjälp av TotalRequestCharge egenskapen i ServerSideCumulativeMetrics eller så kan du titta på begärandeavgiften från varje partition med hjälp av RequestCharge egenskapen för varje ServerSidePartitionedMetrics returnerad.

Den totala kostnaden för begäran är också tillgänglig med hjälp av RequestCharge egenskapen i FeedResponse. Mer information om hur du hämtar begärandeavgiften med hjälp av Azure-portalen och olika SDK:er finns i artikeln om enhetsdebitering för begäran.

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);
}

Hämta frågekörningstiden

Du kan samla in körningstid för frågor för varje resa från frågemåtten. När du tittar på svarstid för begäran är det viktigt att skilja frågekörningstiden från andra svarstider, till exempel tid för nätverksöverföring. I följande exempel visas hur du hämtar kumulativ frågekörningstid för varje tur och retur-resa:

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);

Hämta indexanvändningen

Om du tittar på indexanvändningen kan du felsöka långsamma frågor. Frågor som inte kan använda indexet resulterar i en fullständig genomsökning av alla dokument i en container innan resultatuppsättningen returneras.

Här är ett exempel på en genomsökningsfråga:

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

Filtret för den här frågan använder systemfunktionen UPPER, som inte hanteras från indexet. När den här frågan kördes mot en stor samling skapades följande frågemått för den första fortsättningen:

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

Observera följande värden från frågemåttens utdata:

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

Den här frågan läste in 60 951 dokument, vilket uppgick till 399 998 938 byte. Om du läser in så här många byte blir kostnaden hög eller enhetsavgiften för begäranden. Det tar också lång tid att köra frågan, vilket är tydligt med den totala tidsåtgången för egenskapen:

Total Query Execution Time               :        4,500.34 milliseconds

Det tog 4,5 sekunder att köra frågan (och det var bara en fortsättning).

Om du vill optimera den här exempelfrågan undviker du användningen av UPPER i filtret. När dokument skapas eller uppdateras c.description måste värdena i stället infogas i alla versaler. Frågan blir sedan:

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

Den här frågan kan nu hanteras från indexet. Du kan också använda beräknade egenskaper för att indexeras resultatet av systemfunktioner eller komplexa beräkningar som annars skulle resultera i en fullständig genomsökning.

Mer information om hur du justerar frågeprestanda finns i artikeln Finjustera frågeprestanda .

Referenser

Nästa steg