Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
ОБЛАСТЬ ПРИМЕНЕНИЯ: NoSQL
В этой статье показано, как профилировать производительность sql-запросов в Azure Cosmos DB с помощью ServerSideCumulativeMetrics , полученных из пакета SDK для .NET.
ServerSideCumulativeMetrics
— это строго типизированный объект с информацией о выполнении внутреннего запроса. Он содержит накопительные метрики, агрегированные во всех физических секциях запроса, список метрик для каждой физической секции и общая плата за запрос. Эти метрики более подробно описаны в статье Настройка производительности запросов.
Получение метрик запроса
Метрики запросов доступны в виде строго типизированного объекта в пакете SDK для .NET, начиная с версии 3.36.0. До этой версии, или если вы используете другой язык SDK, можно получить метрики запросов, разобрав Diagnostics
. В следующем примере кода показано, как получить ServerSideCumulativeMetrics
из Diagnostics
в FeedResponse:
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
, или можете просмотреть плату за запрос для каждого раздела, используя свойство RequestCharge
для каждого возвращенного ServerSidePartitionedMetrics
.
Общая плата за запрос также доступна, используя свойство RequestCharge
в FeedResponse
. Дополнительные сведения о том, как определить стоимость единиц запроса с помощью портала 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"
Теперь этот запрос может быть обслужен из индекса. Кроме того, вы можете использовать вычисляемые свойства для индексирования результатов системных функций или сложных вычислений, которые в противном случае приводят к полному сканированию.
Дополнительные сведения о настройке производительности запросов см. в статье Настройка производительности запросов.