共用方式為


使用 .NET SDK 取得 SQL 查詢執行計量和分析查詢效能

適用於:NoSQL

本文呈現如何使用從 .NET SDK 擷取的 ServerSideCumulativeMetrics 以在 Azure Cosmos DB 上分析 SQL 查詢效能。 ServerSideCumulativeMetrics 是強型別物件,具有後端查詢執行的相關資訊。 它包含累計計量,這些計量會匯總至要求的所有實體分割區、每個實體分割區的計量清單,以及要求費用總計。 微調查詢效能一文詳述這些計量。

取得查詢計量

3.36.0 版開始的 .NET SDK 中,查詢計量可作為強型別物件。 在此版本之前,或如果您要使用不同的 SDK 語言,則可以剖析 Diagnostics 以擷取查詢計量。 下列程式碼範例顯示如何從 FeedResponse 中的 Diagnostics 擷取 ServerSideCumulativeMetrics

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

您也可以使用 ToFeedIterator() 方法以從 LINQ 查詢的 FeedResponse 取得查詢計量:

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

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

累計計量

ServerSideCumulativeMetrics 包含 CumulativeMetrics 屬性,而此屬性代表針對單一來回行程的所有分割區所彙總的查詢計量。

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

您也可以跨查詢的所有來回行程來彙總這些計量。 下列範例顯示如何使用 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(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);

已分割的計量

ServerSideCumulativeMetrics 包含 PartitionedMetrics 屬性,而此屬性是來回行程的每個分割區計量清單。 如果單一來回行程中達到多個實體分割區,則其都會出現在清單中。 數據分割計量會以 ServerSidePartitionedMetrics 表示,每個實體分割區的唯一標識符,以及該分割區的要求費用。

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

針對所有來回行程累計時,每個分割區計量可讓您與其他分割區進行比較,以查看效能問題是否起因於特定分割區。 下列範例顯示如何使用 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(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();
    }
}

取得查詢要求費用

您可以擷取每個查詢所耗用的要求單位,以調查耗費資源的查詢,或耗用高輸送量的查詢。 您可以使用 中的 ServerSideCumulativeMetrics 屬性取得要求費用TotalRequestCharge總計,也可以使用每個傳回的屬性查看每個ServerSidePartitionedMetrics分割RequestCharge區的要求費用。

您也可以使用 中的FeedResponse屬性來取得RequestCharge要求費用總計。 若要深入了解如何使用 Azure 入口網站和不同 SDK 來取得要求費用,請參閱尋找要求單位費用一文。

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

取得查詢執行時間

您可以從查詢計量中擷取每個行程的查詢執行時間。 查看要求延遲時,請務必區分查詢執行時間與其他延遲來源,例如網路傳輸時間。 下列範例顯示如何取得每個來回行程的累計查詢執行時間:

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

取得索引使用率

查看索引使用率可協助您偵錯緩慢的查詢。 傳回結果集之前,無法使用容器中所有文件完整掃描的索引結果的查詢。

以下是掃描查詢的範例:

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

此查詢的篩選條件使用未經索引處理的系統函數 UPPER。 針對大型集合執行此查詢時,第一次延續會產生下列查詢計量:

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

在查詢計量輸出中,請注意下列值:

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

此查詢載入 60,951 個文件,總計 399,998,938 個位元組。 載入如此多位元組導致很高的成本或要求單位費用。 執行此查詢也耗時甚久,從花費總時間屬性即可明白:

Total Query Execution Time               :        4,500.34 milliseconds

表示執行查詢花 4.5 秒 (這只是一次延續)。

若要將此範例查詢最佳化,請避免在篩選條件中使用 UPPER。 相反地,在建立或更新文件時,必須以全部大寫字元插入 c.description 值。 於是查詢變成:

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

此查詢現在可以經過索引處理。 或者,您可以使用計算屬性來編製系統函數或複雜計算結果的索引,否則會導致完整掃描。

若要深入了解微調查詢效能,請參閱微調查詢效能一文。

參考資料

下一步