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 Diagnostics
te parseren. In het volgende codevoorbeeld ziet u hoe u deze kunt ophalen ServerSideCumulativeMetrics
uit de Diagnostics
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();
}
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(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);
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(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();
}
}
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 .