Mendapatkan metrik eksekusi kueri SQL dan menganalisis kinerja kueri menggunakan .NET SDK
BERLAKU UNTUK: SQL API
Artikel ini menyajikan cara membuat profil kinerja kueri SQL di Azure Cosmos DB. Pembuatan profil ini dapat dilakukan menggunakan QueryMetrics
yang diambil dari SDK .NET dan dijelaskan secara mendetail di sini. QueryMetrics adalah objek dengan batasan penulisan ketat, berisi informasi tentang eksekusi kueri ujung belakang. Metrik ini didokumentasikan secara lebih mendetail dalam artikel Menyelaraskan Kinerja Kueri.
Mengatur parameter FeedOptions
Semua kelebihan beban untuk DocumentClient.CreateDocumentQuery mengambil parameter FeedOptions opsional. Opsi inilah yang memungkinkan eksekusi kueri diselaraskan dan dibuat parameternya.
Untuk mengumpulkan metrik eksekusi kueri Sql, Anda harus mengatur parameter PopulateQueryMetrics dalam FeedOptions ke true
. Mengatur PopulateQueryMetrics
ke true akan membuat FeedResponse
berisi QueryMetrics
yang relevan.
Mendapatkan metrik kueri dengan AsDocumentQuery()
Contoh kode berikut menunjukkan cara mengambil metrik saat menggunakan metode AsDocumentQuery():
// Initialize this DocumentClient and Collection
DocumentClient documentClient = null;
DocumentCollection collection = null;
// Setting PopulateQueryMetrics to true in the FeedOptions
FeedOptions feedOptions = new FeedOptions
{
PopulateQueryMetrics = true
};
string query = "SELECT TOP 5 * FROM c";
IDocumentQuery<dynamic> documentQuery = documentClient.CreateDocumentQuery(Collection.SelfLink, query, feedOptions).AsDocumentQuery();
while (documentQuery.HasMoreResults)
{
// Execute one continuation of the query
FeedResponse<dynamic> feedResponse = await documentQuery.ExecuteNextAsync();
// This dictionary maps the partitionId to the QueryMetrics of that query
IReadOnlyDictionary<string, QueryMetrics> partitionIdToQueryMetrics = feedResponse.QueryMetrics;
// At this point you have QueryMetrics which you can serialize using .ToString()
foreach (KeyValuePair<string, QueryMetrics> kvp in partitionIdToQueryMetrics)
{
string partitionId = kvp.Key;
QueryMetrics queryMetrics = kvp.Value;
// Do whatever logging you need
DoSomeLoggingOfQueryMetrics(query, partitionId, queryMetrics);
}
}
Menggabungkan QueryMetrics
Di bagian sebelumnya, perhatikan bahwa ada beberapa panggilan ke metode ExecuteNextAsync. Setiap panggilan mengembalikan objek FeedResponse
yang memiliki kamus QueryMetrics
; satu untuk setiap kelanjutan kueri. Contoh berikut menunjukkan cara menggabungkan QueryMetrics
ini menggunakan LINQ:
List<QueryMetrics> queryMetricsList = new List<QueryMetrics>();
while (documentQuery.HasMoreResults)
{
// Execute one continuation of the query
FeedResponse<dynamic> feedResponse = await documentQuery.ExecuteNextAsync();
// This dictionary maps the partitionId to the QueryMetrics of that query
IReadOnlyDictionary<string, QueryMetrics> partitionIdToQueryMetrics = feedResponse.QueryMetrics;
queryMetricsList.AddRange(partitionIdToQueryMetrics.Values);
}
// Aggregate the QueryMetrics using the + operator overload of the QueryMetrics class.
QueryMetrics aggregatedQueryMetrics = queryMetricsList.Aggregate((curr, acc) => curr + acc);
Console.WriteLine(aggregatedQueryMetrics);
Mengelompokkan metrik kueri menurut ID Partisi
Anda dapat mengelompokkan QueryMetrics
menurut ID Partisi. Pengelompokan menurut ID Partisi memungkinkan Anda melihat apakah Partisi tertentu menyebabkan masalah kinerja dibanding yang lain. Contoh berikut menunjukkan cara mengelompokkan QueryMetrics
dengan LINQ:
List<KeyValuePair<string, QueryMetrics>> partitionedQueryMetrics = new List<KeyValuePair<string, QueryMetrics>>();
while (documentQuery.HasMoreResults)
{
// Execute one continuation of the query
FeedResponse<dynamic> feedResponse = await documentQuery.ExecuteNextAsync();
// This dictionary is maps the partitionId to the QueryMetrics of that query
IReadOnlyDictionary<string, QueryMetrics> partitionIdToQueryMetrics = feedResponse.QueryMetrics;
partitionedQueryMetrics.AddRange(partitionIdToQueryMetrics.ToList());
}
// Now we are able to group the query metrics by partitionId
IEnumerable<IGrouping<string, KeyValuePair<string, QueryMetrics>>> groupedByQueryMetrics = partitionedQueryMetrics
.GroupBy(kvp => kvp.Key);
// If we wanted to we could even aggregate the groupedby QueryMetrics
foreach(IGrouping<string, KeyValuePair<string, QueryMetrics>> grouping in groupedByQueryMetrics)
{
string partitionId = grouping.Key;
QueryMetrics aggregatedQueryMetricsForPartition = grouping
.Select(kvp => kvp.Value)
.Aggregate((curr, acc) => curr + acc);
DoSomeLoggingOfQueryMetrics(query, partitionId, aggregatedQueryMetricsForPartition);
}
LINQ di DocumentQuery
Anda juga dapat memperoleh FeedResponse
dari Kueri LINQ menggunakan metode AsDocumentQuery()
:
IDocumentQuery<Document> linqQuery = client.CreateDocumentQuery(collection.SelfLink, feedOptions)
.Take(1)
.Where(document => document.Id == "42")
.OrderBy(document => document.Timestamp)
.AsDocumentQuery();
FeedResponse<Document> feedResponse = await linqQuery.ExecuteNextAsync<Document>();
IReadOnlyDictionary<string, QueryMetrics> queryMetrics = feedResponse.QueryMetrics;
Kueri Mahal
Anda dapat mencatat unit permintaan yang digunakan oleh setiap kueri untuk menyelidiki kueri mahal atau kueri yang menggunakan throughput tinggi. Anda dapat memperoleh biaya permintaan dengan properti RequestCharge di FeedResponse
. Untuk mempelajari selengkapnya tentang cara mendapatkan biaya permintaan menggunakan portal Azure dan SDK lain, lihat artikel menemukan biaya unit permintaan.
string query = "SELECT * FROM c";
IDocumentQuery<dynamic> documentQuery = documentClient.CreateDocumentQuery(Collection.SelfLink, query, feedOptions).AsDocumentQuery();
while (documentQuery.HasMoreResults)
{
// Execute one continuation of the query
FeedResponse<dynamic> feedResponse = await documentQuery.ExecuteNextAsync();
double requestCharge = feedResponse.RequestCharge
// Log the RequestCharge how ever you want.
DoSomeLogging(requestCharge);
}
Mendapatkan waktu eksekusi kueri
Saat menghitung waktu yang diperlukan untuk menjalankan kueri pihak klien, pastikan Anda hanya menyertakan waktu untuk memanggil metode ExecuteNextAsync
dan bukan bagian lain basis kode Anda. Hanya panggilan ini yang membantu Anda menghitung waktu eksekusi kueri berjalan seperti yang ditunjukkan dalam contoh berikut:
string query = "SELECT * FROM c";
IDocumentQuery<dynamic> documentQuery = documentClient.CreateDocumentQuery(Collection.SelfLink, query, feedOptions).AsDocumentQuery();
Stopwatch queryExecutionTimeEndToEndTotal = new Stopwatch();
while (documentQuery.HasMoreResults)
{
// Execute one continuation of the query
queryExecutionTimeEndToEndTotal.Start();
FeedResponse<dynamic> feedResponse = await documentQuery.ExecuteNextAsync();
queryExecutionTimeEndToEndTotal.Stop();
}
// Log the elapsed time
DoSomeLogging(queryExecutionTimeEndToEndTotal.Elapsed);
Kueri pemindaian (umumnya lambat dan mahal)
Kueri pemindaian merujuk ke kueri yang tidak dilayani oleh indeks, karena itu, banyak dokumen dimuat sebelum mengembalikan set hasil.
Di bawah ini adalah contoh kueri pemindaian:
SELECT VALUE c.description
FROM c
WHERE UPPER(c.description) = "BABYFOOD, DESSERT, FRUIT DESSERT, WITHOUT ASCORBIC ACID, JUNIOR"
Filter kueri ini menggunakan fungsi sistem UPPER, yang tidak disajikan dari indeks. Eksekusi kueri ini terhadap kumpulan besar menghasilkan metrik kueri berikut untuk kelanjutan pertama:
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 Times
Query Compilation Time : 0.09 milliseconds
Logical Plan Build Time : 0.05 milliseconds
Physical Plan Build Time : 0.04 milliseconds
Query Optimization Time : 0.01 milliseconds
Index Lookup Time : 0.01 milliseconds
Document Load Time : 4,177.66 milliseconds
Runtime Execution Times
Query Engine Times : 322.16 milliseconds
System Function Execution Time : 85.74 milliseconds
User-defined Function Execution Time : 0.00 milliseconds
Document Write Time : 0.01 milliseconds
Client Side Metrics
Retry Count : 0
Request Charge : 4,059.95 RUs
Perhatikan nilai berikut dari output metrik kueri:
Retrieved Document Count : 60,951
Retrieved Document Size : 399,998,938 bytes
Kueri ini memuat 60.951 dokumen, yang berjumlah 399.998.938 byte. Pemuatan byte sebanyak ini menghasilkan biaya tinggi atau biaya unit permintaan. Diperlukan juga waktu yang lama untuk menjalankan kueri, yang jelas dengan total waktu yang dihabiskan properti:
Total Query Execution Time : 4,500.34 milliseconds
Artinya kueri membutuhkan waktu 4,5 detik untuk berjalan (dan ini hanya satu kelanjutan).
Untuk mengoptimalkan kueri contoh ini, hindari penggunaan UPPER dalam filter. Sebagai gantinya, ketika dokumen dibuat atau diperbarui, nilai c.description
harus disisipkan dalam karakter huruf besar seluruhnya. Kueri kemudian menjadi:
SELECT VALUE c.description
FROM c
WHERE c.description = "BABYFOOD, DESSERT, FRUIT DESSERT, WITHOUT ASCORBIC ACID, JUNIOR"
Kueri ini kini dapat dilayani dari indeks.
Untuk mempelajari selengkapnya tentang penyelarasan kinerja kueri, lihat artikel Menyelaraskan Kinerja Kueri.