Ottenere le metriche di esecuzione delle query SQL e analizzare le prestazioni delle query con .NET SDK

SI APPLICA A: NoSQL

Questo articolo illustra come profilare le prestazioni delle query SQL in Azure Cosmos DB usando ServerSideCumulativeMetrics recuperato da .NET SDK. ServerSideCumulativeMetrics è un oggetto fortemente tipizzato con informazioni sull'esecuzione di query back-end. Contiene metriche cumulative aggregate in tutte le partizioni fisiche per la richiesta e un elenco di metriche per ogni partizione fisica. Queste metriche sono documentate in modo più dettagliato nell'articolo Ottimizzare le prestazioni delle query.

Recuperare le metriche della query

Le metriche delle query sono disponibili come oggetto fortemente tipizzato in .NET SDK a partire dalla versione 3.36.0. Prima di questa versione o se si usa un linguaggio SDK diverso, è possibile recuperare le metriche di query analizzando .Diagnostics L'esempio di codice seguente illustra come recuperare ServerSideCumulativeMetrics da Diagnostics in un oggetto FeedResponse:

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

È anche possibile ottenere metriche di query da FeedResponse di una query LINQ usando il ToFeedIterator() metodo :

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

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

Metriche cumulative

ServerSideCumulativeMetrics contiene una CumulativeMetrics proprietà che rappresenta le metriche di query aggregate in tutte le partizioni per il singolo round trip.

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

È anche possibile aggregare queste metriche in tutti i round trip per la query. Di seguito è riportato un esempio di come aggregare il tempo di esecuzione delle query in tutti i round trip per una determinata query usando 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);

Metriche partizionate

ServerSideCumulativeMetrics contiene una PartitionedMetrics proprietà che rappresenta un elenco di metriche per partizione per il round trip. Se vengono raggiunte più partizioni fisiche in un singolo round trip, le metriche per ognuna di esse vengono visualizzate nell'elenco. Le metriche partizionate sono rappresentate come ServerSidePartitionedMetrics con un identificatore univoco per ogni partizione fisica.

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

Quando vengono accumulati in tutti i round trip, le metriche per partizione consentono di verificare se una partizione specifica causa problemi di prestazioni rispetto ad altri. Di seguito è riportato un esempio di come raggruppare le metriche di partizione per ogni viaggio usando 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();
    }
}

Ottenere l'addebito della richiesta di query

È possibile acquisire le unità richiesta utilizzate da ogni query per analizzare query o query costose che utilizzano una velocità effettiva elevata. È possibile ottenere l'addebito della richiesta usando la RequestCharge proprietà in FeedResponse. Per altre informazioni su come ottenere l'addebito della richiesta usando il portale di Azure e i diversi SDK, vedere l'articolo relativo all'addebito unità richiesta.

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

Ottenere il tempo di esecuzione della query

È possibile acquisire il tempo di esecuzione delle query per ogni corsa dalle metriche di query. Quando si esamina la latenza delle richieste, è importante distinguere il tempo di esecuzione delle query da altre origini di latenza, ad esempio il tempo di transito di rete. L'esempio seguente illustra come ottenere il tempo di esecuzione cumulativo delle query per ogni round trip:

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

Ottenere l'utilizzo dell'indice

L'analisi dell'utilizzo dell'indice consente di eseguire il debug di query lente. Le query che non possono usare il risultato dell'indice in un'analisi completa di tutti i documenti in un contenitore prima di restituire il set di risultati.

Ecco un esempio di query di analisi:

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

Il filtro di questa query usa la funzione di sistema UPPER, che non viene servita dall'indice. L'esecuzione di questa query su una raccolta di grandi dimensioni ha prodotto le metriche di query seguenti per la prima continuazione:

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

Si notino i valori seguenti dell'output delle metriche di query:

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

Questa query ha caricato 60.951 documenti, che hanno totalmente 399.998.938 byte. Il caricamento di questo numero di byte comporta costi elevati o addebiti per unità richiesta. L'esecuzione della query richiede molto tempo, che risulta chiara con la proprietà totale impiegato:

Total Query Execution Time               :        4,500.34 milliseconds

Ciò significa che la query ha richiesto 4,5 secondi per l'esecuzione (e questa era una sola continuazione).

Per ottimizzare questa query di esempio, evitare l'uso di UPPER nel filtro. Al contrario, quando i documenti vengono creati o aggiornati, i c.description valori devono essere inseriti in tutti i caratteri maiuscoli. La query diventa quindi:

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

Questa query è ora in grado di essere servita dall'indice. In alternativa, è possibile usare le proprietà calcolate per indicizzare i risultati delle funzioni di sistema o calcoli complessi che altrimenti generano un'analisi completa.

Per altre informazioni sull'ottimizzazione delle prestazioni delle query, vedere l'articolo Ottimizzare le prestazioni delle query.

Riferimenti

Passaggi successivi