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
각 파티션의 요청 요금을 확인할 수 있습니다.
총 요청 요금은 .의 RequestCharge
FeedResponse
속성을 사용하여 사용할 수도 있습니다. 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"
그러면 이제 이 쿼리를 인덱스에서 제공할 수 있습니다. 또는 계산 속성을 사용하여 시스템 함수 또는 복잡한 계산의 결과를 인덱싱할 수 있습니다. 그렇지 않으면 전체 검색이 발생합니다.
쿼리 성능 조정에 대한 자세한 내용은 쿼리 성능 조정 문서를 참조하세요.