다음을 통해 공유


SQL 쿼리 실행 메트릭 가져오기 및 .NET SDK을 사용하여 쿼리 성능 분석하기

적용 대상: NoSQL

이 문서에서는 .NET SDK에서 검색된 ServerSideCumulativeMetrics을 사용하여 Azure Cosmos DB에서 SQL 쿼리 성능을 프로파일하는 방법을 보여 줍니다. ServerSideCumulativeMetrics는 백엔드 쿼리 실행에 대한 정보를 포함하는 엄격히 형식화된 객체입니다. 여기에는 요청에 대한 모든 실제 파티션에서 집계되는 누적 메트릭과 각 실제 파티션에 대한 메트릭 목록 및 총 요청 요금이 포함됩니다. 해당 메트릭은 쿼리 성능 조정 문서에서 보다 자세히 알아볼 수 있습니다.

쿼리 메트릭 가져오기

쿼리 메트릭은 버전 3.36.0에서부터 .NET SDK에서 강력한 형식의 개체로 사용할 수 있습니다. 이 버전 이전 또는 다른 SDK 언어를 사용하는 경우 Diagnostics 구문을 분석하여 쿼리 메트릭을 검색할 수 있습니다. 다음 코드 샘플에서는 ServerSideCumulativeMetrics에서 Diagnostics(을)를 으로부터 검색하는 방법을 보여줍니다.

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

FeedResponse 메서드를 사용하여 LINQ 쿼리의 ToFeedIterator() 쿼리 메트릭을 가져올 수도 있습니다.

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

분할된 메트릭

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

쿼리 요청 요금 가져오기

각 쿼리에서 사용하는 요청 단위를 캡처하여 비용이 높은 쿼리나 높은 처리량을 사용하는 쿼리를 조사할 수 있습니다. TotalRequestCharge 속성을 사용하여 ServerSideCumulativeMetrics에서 총 요청 요금을 얻을 수 있습니다. 또한 반환된 각 ServerSidePartitionedMetrics에 대해 RequestCharge 속성을 사용하여 각 파티션의 요청 요금을 확인할 수 있습니다.

총 요청 요금은 .의 RequestChargeFeedResponse속성을 사용하여 사용할 수도 있습니다. Azure Portal과 기타 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

이 쿼리는 총 399,998,938바이트의 60,951개 문서를 로드했습니다. 이렇듯 많은 양의 바이트를 로드하면 높은 비용 또는 요청 단위 요금이 발생합니다. 또한 총 시간 사용 속성에서 명확히 확인할 수 있듯 쿼리 실행에 더 오랜 시간이 걸립니다:

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"

그러면 이제 이 쿼리를 인덱스에서 제공할 수 있습니다. 또는 계산 속성을 사용하여 시스템 함수 또는 복잡한 계산의 결과를 인덱싱할 수 있습니다. 그렇지 않으면 전체 검색이 발생합니다.

쿼리 성능 조정에 대한 자세한 내용은 쿼리 성능 조정 문서를 참조하세요.

참조

다음 단계