.NET SDK kullanarak SQL sorgu yürütme ölçümlerini alma ve sorgu performansını analiz etme

UYGULANANLAR: NoSQL

Bu makalede, .NET SDK'sından alınan ServerSideCumulativeMetrics kullanılarak Azure Cosmos DB'de SQL sorgu performansının profilinin nasıl çıkarıldığı açıklanmaktadır. ServerSideCumulativeMetrics , arka uç sorgu yürütmesi hakkında bilgi içeren kesin olarak belirlenmiş bir nesnedir. İstek için tüm fiziksel bölümlerde toplanan toplu ölçümler, her fiziksel bölüm için bir ölçüm listesi ve toplam istek ücreti içerir. Bu ölçümler Sorgu Performansını Ayarlama makalesinde daha ayrıntılı olarak belgelenmiştir.

Sorgu ölçümlerini alma

Sorgu ölçümleri, 3.36.0 sürümünden başlayarak .NET SDK'da kesin olarak yazılan bir nesne olarak kullanılabilir. Bu sürümden önce veya farklı bir SDK dili kullanıyorsanız sorgu ölçümlerini almak için öğesini Diagnosticsayrıştırabilirsiniz. Aşağıdaki kod örneği, bir FeedResponse içindeki öğesinden Diagnostics nasıl alınacaklarını ServerSideCumulativeMetrics gösterir:

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

Aşağıdaki yöntemi kullanarak ToFeedIterator() bir LINQ sorgusundan FeedResponse sorgu ölçümleri de alabilirsiniz:

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

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

Kümülatif Ölçümler

ServerSideCumulativeMetrics tek gidiş dönüş için tüm bölümler üzerinde toplanan sorgu ölçümlerini temsil eden bir CumulativeMetrics özellik içerir.

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

Bu ölçümleri sorgu için tüm gidiş dönüşlerde de toplayabilirsiniz. Aşağıda, LINQ kullanarak belirli bir sorgu için tüm gidiş dönüşlerde sorgu yürütme süresini toplama örneği verilmiştir:

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(response.Diagnostics.GetQueryMetrics());
}

// Aggregate values across trips for metrics of interest
TimeSpan totalTripsExecutionTime = metrics.Aggregate(TimeSpan.Zero, (currentSum, next) => currentSum + next.CumulativeMetrics.TotalTime);
DoSomeLogging(totalTripsExecutionTime);

Bölümlenmiş Ölçümler

ServerSideCumulativeMetrics gidiş dönüş için bölüm başına ölçümlerin listesi olan bir PartitionedMetrics özellik içerir. Tek bir gidiş dönüşte birden çok fiziksel bölüme ulaşılırsa, bunların her biri için ölçümler listede görünür. Bölümlenmiş ölçümler, her fiziksel bölüm için benzersiz tanımlayıcıya sahip ServerSidePartitionedMetrics olarak temsil edilir ve bu bölüm için ücret talep edilir.

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

Tüm gidiş dönüşlerde biriktiğinde bölüm başına ölçümler, belirli bir bölümün diğerlerine kıyasla performans sorunlarına neden olup olmadığını görmenizi sağlar. Aşağıda LINQ kullanarak her yolculuk için bölüm ölçümlerini gruplandırma örneği verilmiştir:

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

Sorgu isteği ücretini alma

Yüksek aktarım hızı kullanan pahalı sorguları veya sorguları araştırmak için her sorgu tarafından kullanılan istek birimlerini yakalayabilirsiniz. içindeki özelliğini ServerSideCumulativeMetrics kullanarak TotalRequestCharge toplam istek ücretini alabilir veya döndürülen her ServerSidePartitionedMetrics bölüm için özelliğini kullanarak her bölümden istek ücretine RequestCharge bakabilirsiniz.

Toplam istek ücreti, içindeki FeedResponseözelliği kullanılarak RequestCharge da kullanılabilir. Azure portalını ve farklı SDK'ları kullanarak istek ücretini alma hakkında daha fazla bilgi edinmek için istek birimi ücreti makalesine bakın.

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

Sorgu yürütme süresini alma

Sorgu ölçümlerinden her yolculuk için sorgu yürütme süresini yakalayabilirsiniz. İstek gecikme süresine bakarken, sorgu yürütme süresini ağ aktarım süresi gibi diğer gecikme süresi kaynaklarından ayırt etmek önemlidir. Aşağıdaki örnekte, her gidiş dönüş için toplu sorgu yürütme süresinin nasıl alınacakları gösterilmektedir:

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

Dizin kullanımını alma

Dizin kullanımına bakmak yavaş sorgularda hata ayıklamanıza yardımcı olabilir. Dizin kullanabilen sorgular, sonuç kümesini döndürmeden önce kapsayıcıdaki tüm belgelerin tam taramasıyla sonuçlanır.

Tarama sorgusu örneği aşağıda verilmişti:

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

Bu sorgunun filtresi, dizinden sunulmayan UPPER sistem işlevini kullanır. Büyük bir koleksiyonda bu sorgunun yürütülmesi, ilk devam için aşağıdaki sorgu ölçümlerini üretti:

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

Sorgu ölçümleri çıkışından aşağıdaki değerlere dikkat edin:

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

Bu sorgu, toplam 399.998.938 bayt olan 60.951 belge yükledi. Bu kadar bayt yüklenmesi yüksek maliyet veya istek birimi ücretine neden olur. Sorgunun yürütülmesi de uzun sürer ve bu işlem harcanan toplam süre özelliğiyle nettir:

Total Query Execution Time               :        4,500.34 milliseconds

Bu, sorgunun yürütülmesinin 4,5 saniye sürdüğü anlamına gelir (ve bu yalnızca bir devamlılıktı).

Bu örnek sorguyu iyileştirmek için filtrede UPPER kullanmaktan kaçının. Bunun yerine, belgeler oluşturulduğunda veya güncelleştirildiğinde, değerlerin c.description tüm büyük harflerle eklenmesi gerekir. Sorgu şu hale gelir:

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

Bu sorgu artık dizinden sunulabiliyor. Alternatif olarak, sistem işlevlerinin sonuçlarını veya tam taramayla sonuçlanan karmaşık hesaplamaları dizine almak için hesaplanan özellikleri kullanabilirsiniz.

Sorgu performansını ayarlama hakkında daha fazla bilgi edinmek için Sorgu Performansını Ayarlama makalesine bakın.

Başvurular

Sonraki adımlar