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í backendového dotazu. Obsahuje souhrnné metriky agregované napříč všemi fyzickými oddíly pro požadavek, seznam metrik pro každý fyzický oddíl a celkové náklady na žádost. Tyto metriky jsou podrobněji popsány v článku Ladění výkonu dotazu.

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

Metriky dotazů můžete také získat z FeedResponse dotazu LINQ pomocí metody ToFeedIterator().

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 obsahuje vlastnost CumulativeMetrics, která představuje metriky dotazů agregované ve všech oddílech pro jeden přenos.

// 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 oběhy dotazu. Následuje příklad, jak agregovat dobu provádění dotazu napříč všemi zpátečními cestami 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 obsahuje vlastnost PartitionedMetrics, která je seznamem metrik pro jednotlivé oddíly pro okružní cestu. Pokud při jednom cyklu dojde ke kontaktu s více fyzickými oddíly, 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 náklady na žádost pro 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 TotalRequestCharge můžete získat celkový poplatek za žádost v kontextu ServerSideCumulativeMetrics, nebo se můžete podívat na poplatek za žádost pro každý oddíl pomocí vlastnosti RequestCharge pro každou vrácenou ServerSidePartitionedMetrics.

Celkové poplatky za žádost jsou k dispozici také pomocí RequestCharge vlastnosti v FeedResponse. Další informace o tom, jak získat poplatek za jednotku žádosti pomocí Azure portálu a různých sad SDK, najdete v článku o poplatku 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ískat dobu 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é nemohou použít index, vedou k úplnému skenování 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é z vlastnosti celkového času stráveného:

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