Freigeben über


Abrufen von SQL-Abfrageausführungsmetriken und Analysieren der Abfrageleistung mit dem .NET SDK

GILT FÜR: NoSQL

In diesem Artikel wird erläutert, wie Sie mithilfe der über das .NET SDK abgerufenen Metrik ServerSideCumulativeMetricsein Profil für die SQL-Abfrageleistung in Azure Cosmos DB erstellen. ServerSideCumulativeMetrics ist ein stark typisiertes Objekt mit Informationen zur Back-End-Abfrageausführung. Es enthält kumulative Metriken, die für alle physischen Partitionen für die Anforderung aggregiert werden, eine Liste der Metriken für jede physische Partition und die Gesamtkosten der Anforderung. Diese Metriken werden im Artikel Optimieren der Abfrageleistung ausführlicher dokumentiert.

Abrufen von Abfragemetriken

Abfragemetriken sind ab Version 3.36.0 als stark typisiertes Objekt im .NET SDK verfügbar. Vor dieser Version oder bei Verwendung einer anderen SDK-Sprache können Sie Abfragemetriken abrufen, indem Sie Diagnostics analysieren. Das folgende Codebeispiel zeigt, wie Sie ServerSideCumulativeMetrics aus Diagnostics in FeedResponse abrufen:

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

Sie können Abfragemetriken aus dem FeedResponse-Element einer LINQ-Abfrage auch mithilfe der ToFeedIterator()-Methode abrufen:

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

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

Kumulative Metriken

ServerSideCumulativeMetrics enthält eine CumulativeMetrics-Eigenschaft, die die Abfragemetriken darstellt, die für alle Partitionen für den einzelnen Roundtrip aggregiert wurden.

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

Sie können diese Metriken auch für alle Roundtrips für die Abfrage aggregieren. Im Folgenden sehen Sie ein Beispiel für das Aggregieren der Abfrageausführungszeit für alle Roundtrips für eine bestimmte Abfrage mithilfe von 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);

Partitionierte Metriken

ServerSideCumulativeMetrics enthält eine PartitionedMetrics-Eigenschaft, die eine Liste der Metriken pro Partition für den Roundtrip ist. Wenn mehrere physische Partitionen in einem einzelnen Roundtrip erreicht werden, werden Metriken für jede dieser Partitionen in der Liste angezeigt. Partitionierte Metriken werden als ServerSidePartitionedMetrics mit einem eindeutigen Bezeichner für jede physische Partition und Anforderungskosten für diese Partition dargestellt.

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

Durch das Akkumulieren für alle Roundtrips können Sie mithilfe der Metriken pro Partition feststellen, ob eine bestimmte Partition im Vergleich zu anderen Leistungsprobleme verursacht. Im Folgenden sehen Sie ein Beispiel für das Gruppieren von Partitionsmetriken für jeden Roundtrip mithilfe von 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();
    }
}

Abrufen der Anforderungsgebühren für Abfragen

Sie können die von jeder Abfrage verbrauchten Anforderungseinheiten erfassen, um ressourcenintensive Abfragen oder Abfragen zu untersuchen, die einen hohen Durchsatz nutzen. Sie können die Gesamtkosten der Anforderung mithilfe der TotalRequestCharge-Eigenschaft in ServerSideCumulativeMetrics erhalten, oder Sie können die Anforderungsgebühr von jeder Partition anhand der Eigenschaft RequestCharge für jede zurückgegebene ServerSidePartitionedMetrics anzeigen.

Die Gesamtkosten der Anforderung sind auch mit der RequestCharge-Eigenschaft in FeedResponse verfügbar. Weitere Informationen zum Abrufen der Anforderungsgebühr mithilfe des Azure-Portals und verschiedener SDKs finden Sie im Artikel Ermitteln der Gebühr für eine Anforderungseinheit.

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

Abrufen der Abfrageausführungsdauer

Sie können die Abfrageausführungszeit für jeden Roundtrip aus den Abfragemetriken abrufen. Beim Betrachten der Anforderungslatenz ist es wichtig, die Abfrageausführungszeit von anderen Latenzquellen zu unterscheiden, z. B. Netzwerkübertragungszeit. Das folgende Beispiel zeigt, wie Sie die kumulative Abfrageausführungszeit für jeden Roundtrip abrufen:

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

Abrufen der Indexauslastung

Die Indexauslastung kann Sie beim Debuggen langsamer Abfragen unterstützen. Bei Abfragen, die das Indexergebnis nicht verwenden können, wird eine vollständige Überprüfung aller Dokumente in einem Container ausgeführt, bevor das Resultset zurückgegeben wird.

Hier ein Beispiel einer Überprüfungsabfrage:

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

Der Filter dieser Abfrage verwendet die Systemfunktion UPPER, die nicht über den Index verarbeitet wird. Die Ausführung dieser Abfrage für eine große Sammlung ergab die folgenden Abfragemetriken für die erste Fortsetzung:

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

Beachten Sie die folgenden Werte der Ausgabe der Abfragemetriken:

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

Diese Abfrage hat 60.951 Dokumente geladen, die insgesamt 399.998.938 Bytes umfassten. Das Laden so vieler Bytes führt zu hohen Kosten oder Gebühren für Anforderungseinheiten. Außerdem nimmt die Ausführung der Abfrage viel Zeit in Anspruch, was anhand der Eigenschaft für die insgesamt benötigte Zeit („Total Query Execution Time“) deutlich wird:

Total Query Execution Time               :        4,500.34 milliseconds

Dies bedeutet, dass die Ausführung der Abfrage 4,5 Sekunden dauerte (und das war nur eine Fortsetzung).

Vermeiden Sie die Verwendung von UPPER im Filter, um diese Beispielabfrage zu optimieren. Wenn Dokumente erstellt oder aktualisiert werden, müssen stattdessen die Werte von c.description in Großbuchstaben eingefügt werden. Die Abfrage sieht dann wie folgt aus:

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

Diese Abfrage kann jetzt über den Index verarbeitet werden. Alternativ können Sie berechnete Eigenschaften verwenden, um die Ergebnisse von Systemfunktionen oder komplexen Berechnungen zu indizieren, die andernfalls zu einer vollständigen Überprüfung führen würden.

Weitere Informationen zum Optimieren der Abfrageleistung finden Sie im Artikel Optimieren der Abfrageleistung.

References

Nächste Schritte