다음을 통해 공유


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

적용 대상: NoSQL

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

쿼리 메트릭 가져오기

쿼리 메트릭은 버전 3.36.0에서부터 .NET SDK에서 강력한 형식의 개체로 사용할 수 있습니다. 이 버전 이전 또는 다른 SDK 언어를 사용하는 경우 Diagnostics 구문을 분석하여 쿼리 메트릭을 검색할 수 있습니다. 다음 코드 샘플에서는 FeedResponse에서 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();
}

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

쿼리 요청 요금 가져오기

각 쿼리에서 사용하는 요청 단위를 캡처하여 비용이 높은 쿼리나 높은 처리량을 사용하는 쿼리를 조사할 수 있습니다. 속성을 ServerSideCumulativeMetrics 사용하여 TotalRequestCharge 총 요청 요금을 받거나 반환된 각 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"

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

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

참조

다음 단계