Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
DOTYCZY: NoSQL
W tym artykule przedstawiono sposób profilowania wydajności zapytań SQL w usłudze Azure Cosmos DB przy użyciu parametrów ServerSideCumulativeMetrics pobranych z zestawu .NET SDK.
ServerSideCumulativeMetrics
jest silnie typizowanym obiektem zawierającym informacje o wykonywaniu zapytania backendu. Zawiera ona skumulowane metryki, które są agregowane dla żądania we wszystkich partycjach fizycznych, listę metryk dla każdej partycji fizycznej i całkowitą opłatę za żądanie. Te metryki są szczegółowo opisane w artykule Optymalizacja wydajności zapytań.
Pobieranie metryk zapytania
Metryki zapytań są dostępne jako silnie typizowane obiekty w zestawie .NET SDK, począwszy od wersji 3.36.0. Przed tą wersją lub jeśli używasz innego języka zestawu SDK, możesz pobrać metryki zapytań, analizujejąc element Diagnostics
. Poniższy przykładowy kod pokazuje, jak pobrać ServerSideCumulativeMetrics
z elementu Diagnostics
w obiekcie 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();
}
Metryki zapytań można również pobrać z FeedResponse
zapytania LINQ przy użyciu ToFeedIterator()
metody :
FeedIterator<MyClass> feedIterator = container.GetItemLinqQueryable<MyClass>()
.Take(5)
.ToFeedIterator();
while (feedIterator.HasMoreResults)
{
FeedResponse<MyClass> feedResponse = await feedIterator.ReadNextAsync();
ServerSideCumulativeMetrics metrics = feedResponse.Diagnostics.GetQueryMetrics();
}
Metryki skumulowane
ServerSideCumulativeMetrics
zawiera CumulativeMetrics
właściwość reprezentującą metryki zapytania zagregowane we wszystkich partycjach w czasie pojedynczej podróży.
// 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;
Te metryki można również agregować we wszystkich rundach zapytania. Poniżej przedstawiono przykład sposobu agregowania czasu wykonywania zapytania we wszystkich rundach dla danego zapytania przy użyciu 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);
Metryki partycjonowane
ServerSideCumulativeMetrics
zawiera właściwość, która jest listą PartitionedMetrics
metryk dla poszczególnych partycji w podróży w obie strony. Jeśli w jednej rundzie zostanie osiągniętych wiele partycji fizycznych, na liście zostaną wyświetlone metryki dla każdej z nich. Partycjonowane metryki są reprezentowane jako ServerSidePartitionedMetrics z unikatowym identyfikatorem dla każdej partycji fizycznej i opłatą za żądanie dla tej partycji.
// 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;
Gdy zostaną zebrane dla wszystkich podróży tam i z powrotem, metryki dla poszczególnych partycji umożliwiają sprawdzenie, czy określona partycja powoduje problemy z wydajnością w porównaniu do innych. Poniżej przedstawiono przykład grupowania metryk partycji dla każdej podróży przy użyciu 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();
}
}
Pobieranie opłaty za żądanie zapytania
Jednostki żądań używane przez każde zapytanie można przechwycić w celu zbadania kosztownych zapytań lub zapytań korzystających z wysokiej przepływności. Możesz uzyskać łączną opłatę za żądanie przy użyciu właściwości TotalRequestCharge
w ServerSideCumulativeMetrics
lub sprawdzić opłatę za żądanie z każdej partycji przy użyciu właściwości RequestCharge
dla każdego zwróconego ServerSidePartitionedMetrics
.
Łączna opłata za żądanie jest również dostępna przy użyciu właściwości RequestCharge
w FeedResponse
. Aby dowiedzieć się więcej na temat sposobu pobierania opłaty za żądanie przy użyciu portalu Azure i różnych zestawów SDK, zobacz artykuł o odnalezieniu opłaty za jednostkę żądania.
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);
}
Pobieranie czasu wykonywania zapytania
Możesz uzyskać czas wykonywania zapytań dla każdej podróży z metryk zapytania. Podczas przeglądania opóźnienia żądań ważne jest, aby odróżnić czas wykonywania zapytania od innych źródeł opóźnienia, takich jak czas tranzytowy sieci. W poniższym przykładzie pokazano, jak uzyskać skumulowany czas wykonywania zapytania dla każdej rundy:
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);
Uzyskaj wykorzystanie indeksu
Przyjrzenie się wykorzystaniu indeksów może pomóc w analizowaniu przyczyn wolnego działania zapytań. Zapytania, które nie mogą używać indeksu, powodują pełne skanowanie wszystkich dokumentów w kontenerze przed zwróceniem zestawu wyników.
Oto przykład zapytania skanowania:
SELECT VALUE c.description
FROM c
WHERE UPPER(c.description) = "BABYFOOD, DESSERT, FRUIT DESSERT, WITHOUT ASCORBIC ACID, JUNIOR"
Filtr tego zapytania używa funkcji systemowej UPPER, która nie jest obsługiwana przez indeks. Wykonanie tego zapytania względem dużej kolekcji wywołało następujące metryki zapytania dla pierwszej kontynuacji:
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
Zwróć uwagę na następujące wartości z danych wyjściowych metryk zapytania:
Retrieved Document Count : 60,951
Retrieved Document Size : 399,998,938 bytes
To zapytanie załadowało 60 951 dokumentów, w sumie 399 998 938 bajtów. Ładowanie tak dużej liczby bajtów skutkuje wysokimi kosztami lub opłatami za jednostki zapytań. Wykonanie zapytania trwa również długo, co widać w właściwości pokazującej łączny czas wykonania.
Total Query Execution Time : 4,500.34 milliseconds
Oznacza to, że wykonanie zapytania trwało 4,5 sekundy (i było to tylko jedna kontynuacja).
Aby zoptymalizować to przykładowe zapytanie, należy unikać używania funkcji UPPER w filtrze. Zamiast tego, gdy dokumenty są tworzone lub aktualizowane, wartości c.description
muszą być wstawione przy użyciu wszystkich wielkich liter. Następnie zapytanie staje się następujące:
SELECT VALUE c.description
FROM c
WHERE c.description = "BABYFOOD, DESSERT, FRUIT DESSERT, WITHOUT ASCORBIC ACID, JUNIOR"
To zapytanie może teraz być obsługiwane z indeksu. Alternatywnie można użyć właściwości obliczeniowych do indeksowania wyników funkcji systemowych lub złożonych obliczeń, które w przeciwnym razie spowodują pełne skanowanie.
Aby dowiedzieć się więcej na temat dostrajania wydajności zapytań, zobacz artykuł Dostosowywanie wydajności zapytań.
Bibliografia
- Azure Cosmos DB SQL specification (Specyfikacja języka SQL w usłudze Azure Cosmos DB)
- ANSI SQL 2011
- JSON
- LINQ