Sdílet prostřednictvím


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

ServerSideCumulativeMetricsCumulativeMetrics 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

ServerSideCumulativeMetricsPartitionedMetrics 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ů.

Reference

Další kroky