Získání metrik spouštění dotazů SQL a analýza výkonu dotazů pomocí sady .NET SDK
PLATÍ PRO: NoSQL
Tento článek popisuje, jak profilovat výkon dotazů SQL ve službě Azure Cosmos DB pomocí metrik ServerSideCumulativeMetrics načtených ze sady .NET SDK. ServerSideCumulativeMetrics
je objekt silného typu s informacemi o provádění back-endového dotazu. Obsahuje kumulativní metriky agregované napříč všemi fyzickými oddíly pro požadavek, seznam metrik pro každý fyzický oddíl a celkové poplatky za žádost. Tyto metriky jsou podrobněji popsané v článku Ladění výkonu dotazů.
Získání metrik dotazu
Metriky dotazů jsou k dispozici jako objekt silného typu v sadě .NET SDK počínaje verzí 3.36.0. Před touto verzí nebo pokud používáte jiný jazyk sady SDK, můžete načíst metriky dotazů parsováním Diagnostics
. Následující ukázka kódu ukazuje, jak načíst ServerSideCumulativeMetrics
z 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();
}
Pomocí metody můžete také získat metriky dotazů z FeedResponse
dotazu ToFeedIterator()
LINQ:
FeedIterator<MyClass> feedIterator = container.GetItemLinqQueryable<MyClass>()
.Take(5)
.ToFeedIterator();
while (feedIterator.HasMoreResults)
{
FeedResponse<MyClass> feedResponse = await feedIterator.ReadNextAsync();
ServerSideCumulativeMetrics metrics = feedResponse.Diagnostics.GetQueryMetrics();
}
Kumulativní metriky
ServerSideCumulativeMetrics
CumulativeMetrics
obsahuje vlastnost, která představuje metriky dotazů agregované pro všechny oddíly pro jednu odezvu.
// 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;
Tyto metriky můžete také agregovat napříč všemi dobami odezvy dotazu. Následuje příklad agregace doby provádění dotazů napříč všemi dobami odezvy daného dotazu pomocí 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);
Dělené metriky
ServerSideCumulativeMetrics
PartitionedMetrics
obsahuje vlastnost, která je seznam metrik pro jednotlivé oddíly pro dobu odezvy. Pokud během jedné doby odezvy dosáhnete více fyzických oddílů, zobrazí se v seznamu metriky pro každý z nich. Dělené metriky jsou reprezentovány jako ServerSidePartitionedMetrics s jedinečným identifikátorem pro každý fyzický oddíl a poplatky za tento oddíl.
// 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;
Když se nahromáždí přes všechny doby odezvy, metriky jednotlivých oddílů vám umožňují zjistit, jestli konkrétní oddíl způsobuje problémy s výkonem ve srovnání s ostatními. Následuje příklad seskupení metrik oddílů pro každou cestu pomocí 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();
}
}
Získání poplatku za žádost o dotaz
Jednotky žádostí spotřebované jednotlivými dotazy můžete zachytit, abyste mohli prozkoumat nákladné dotazy nebo dotazy, které spotřebovávají vysokou propustnost. Pomocí vlastnosti ServerSideCumulativeMetrics
můžete získat celkové poplatky TotalRequestCharge
za žádost nebo se můžete podívat na poplatek za žádost z každého oddílu pomocí vlastnosti pro každou ServerSidePartitionedMetrics
vrácenou RequestCharge
hodnotu.
Celkové poplatky za žádost jsou k dispozici také pomocí RequestCharge
vlastnosti v FeedResponse
. Další informace o tom, jak získat poplatek za žádost pomocí webu Azure Portal a různých sad SDK, najdete v článku o poplatcích za jednotku žádosti.
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);
}
Získání doby provádění dotazu
Čas provádění dotazů můžete zaznamenat pro každou cestu z metrik dotazů. Při pohledu na latenci požadavků je důležité rozlišovat dobu provádění dotazů od jiných zdrojů latence, jako je doba přenosu sítě. Následující příklad ukazuje, jak získat kumulativní dobu provádění dotazů pro každou dobu odezvy:
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);
Získání využití indexu
Když se podíváte na využití indexu, můžete ladit pomalé dotazy. Dotazy, které nemůžou použít výsledek indexu, při úplné kontrole všech dokumentů v kontejneru před vrácením sady výsledků
Tady je příklad skenovacího dotazu:
SELECT VALUE c.description
FROM c
WHERE UPPER(c.description) = "BABYFOOD, DESSERT, FRUIT DESSERT, WITHOUT ASCORBIC ACID, JUNIOR"
Filtr tohoto dotazu používá systémovou funkci UPPER, která není obsluhována z indexu. Spuštění tohoto dotazu pro velkou kolekci vytvořilo následující metriky dotazu pro první pokračování:
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
Všimněte si následujících hodnot z výstupu metrik dotazu:
Retrieved Document Count : 60,951
Retrieved Document Size : 399,998,938 bytes
Tento dotaz načetl 60 951 dokumentů, což celkem 399 998 938 bajtů. Načtení tohoto počtu bajtů vede k vysokým nákladům nebo poplatkům za jednotku žádosti. Provedení dotazu také trvá dlouho, což je jasné s celkovým časem stráveným vlastností:
Total Query Execution Time : 4,500.34 milliseconds
To znamená, že spuštění dotazu trvalo 4,5 sekundy (a to bylo jen jedno pokračování).
Pokud chcete tento ukázkový dotaz optimalizovat, vyhněte se použití funkce UPPER ve filtru. Místo toho při vytváření nebo aktualizaci c.description
dokumentů musí být hodnoty vloženy do všech velkých písmen. Dotaz se pak stane:
SELECT VALUE c.description
FROM c
WHERE c.description = "BABYFOOD, DESSERT, FRUIT DESSERT, WITHOUT ASCORBIC ACID, JUNIOR"
Tento dotaz je teď možné obsluhovat z indexu. Případně můžete pomocí vypočítaných vlastností indexovat výsledky systémových funkcí nebo složitých výpočtů, které by jinak mohly vést k úplnému prohledávání.
Další informace o ladění výkonu dotazů najdete v článku Ladění výkonu dotazů.