Bagikan melalui


Diagnosis Performa

Bagian ini membahas cara untuk mendeteksi masalah performa di aplikasi EF Anda, dan setelah area bermasalah diidentifikasi, cara menganalisisnya lebih lanjut untuk mengidentifikasi masalah akar. Penting untuk mendiagnosis dan menyelidiki masalah dengan hati-hati sebelum melompat ke kesimpulan apa pun, dan untuk menghindari asumsi di mana akar masalah berada.

Mengidentifikasi perintah database lambat melalui pengelogan

Pada akhir hari, EF menyiapkan dan menjalankan perintah untuk dijalankan terhadap database Anda; dengan database relasional, itu berarti menjalankan pernyataan SQL melalui API database ADO.NET. Jika kueri tertentu memakan waktu terlalu banyak (misalnya karena indeks hilang), ini dapat dilihat ditemukan dengan memeriksa log eksekusi perintah dan mengamati berapa lama mereka benar-benar membutuhkan waktu.

EF memudahkan untuk menangkap waktu eksekusi perintah, melalui pengelogan sederhana atau Microsoft.Extensions.Logging:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder
        .UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True;ConnectRetryCount=0")
        .LogTo(Console.WriteLine, LogLevel.Information);
}

Ketika tingkat pengelogan diatur pada LogLevel.Information, EF memancarkan pesan log untuk setiap eksekusi perintah dengan waktu yang diperlukan:

info: 06/12/2020 09:12:36.117 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
      Executed DbCommand (4ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT [b].[Id], [b].[Name]
      FROM [Blogs] AS [b]
      WHERE [b].[Name] = N'foo'

Perintah di atas membutuhkan waktu 4 milidetik. Jika perintah tertentu membutuhkan waktu lebih dari yang diharapkan, Anda telah menemukan kemungkinan pelakunya untuk masalah performa, dan sekarang dapat fokus padanya untuk memahami mengapa itu berjalan lambat. Pengelogan perintah juga dapat mengungkapkan kasus di mana roundtrips database yang tidak terduga sedang dibuat; ini akan muncul sebagai beberapa perintah di mana hanya satu yang diharapkan.

Peringatan

Meninggalkan pengelogan eksekusi perintah yang diaktifkan di lingkungan produksi Anda biasanya merupakan ide yang buruk. Pengelogan itu sendiri memperlambat aplikasi Anda, dan dapat dengan cepat membuat file log besar yang dapat mengisi disk server Anda. Disarankan untuk hanya terus masuk untuk interval waktu singkat untuk mengumpulkan data - sambil memantau aplikasi Anda dengan hati-hati - atau untuk menangkap data pengelogan pada sistem pra-produksi.

Mengkorelasikan perintah database ke kueri LINQ

Salah satu masalah dengan pengelogan eksekusi perintah adalah terkadang sulit untuk menghubungkan kueri SQL dan kueri LINQ: perintah SQL yang dijalankan oleh EF dapat terlihat sangat berbeda dari kueri LINQ dari mana mereka dihasilkan. Untuk membantu dengan kesulitan ini, Anda mungkin ingin menggunakan fitur tag kueri EF, yang memungkinkan Anda menyuntikkan komentar kecil dan mengidentifikasi ke dalam kueri SQL:

var myLocation = new Point(1, 2);
var nearestPeople = (from f in context.People.TagWith("This is my spatial query!")
                     orderby f.Location.Distance(myLocation) descending
                     select f).Take(5).ToList();

Tag muncul di log:

-- This is my spatial query!

SELECT TOP(@__p_1) [p].[Id], [p].[Location]
FROM [People] AS [p]
ORDER BY [p].[Location].STDistance(@__myLocation_0) DESC

Seringkali perlu menandai kueri utama aplikasi dengan cara ini, untuk membuat log eksekusi perintah lebih segera dapat dibaca.

Antarmuka lain untuk menangkap data performa

Ada berbagai alternatif untuk fitur pengelogan EF untuk menangkap waktu eksekusi perintah, yang mungkin lebih kuat. Database biasanya dilengkapi dengan alat pelacakan dan analisis performa mereka sendiri, yang biasanya memberikan informasi khusus database yang jauh lebih kaya di luar waktu eksekusi sederhana; penyiapan, kemampuan, dan penggunaan aktual sangat bervariasi di seluruh database.

Misalnya, SQL Server Management Studio adalah klien canggih yang dapat terhubung ke instans SQL Server Anda dan memberikan informasi manajemen dan performa yang berharga. Ini di luar cakupan bagian ini untuk masuk ke detail, tetapi dua kemampuan yang layak disebutkan adalah Monitor Aktivitas, yang menyediakan dasbor langsung aktivitas server (termasuk kueri paling mahal), dan fitur Extended Events (XEvent), yang memungkinkan menentukan sesi penangkapan data sewenang-wenang yang dapat disesuaikan dengan kebutuhan Anda. Dokumentasi SQL Server tentang pemantauan menyediakan informasi lebih lanjut tentang fitur-fitur ini, serta yang lain.

Pendekatan lain untuk menangkap data performa adalah mengumpulkan informasi yang secara otomatis dipancarkan oleh EF atau driver database melalui antarmuka, lalu menganalisis data tersebut DiagnosticSource atau menampilkannya di dasbor. Jika Anda menggunakan Azure, Azure Application Insights menyediakan pemantauan canggih seperti itu, mengintegrasikan performa database dan waktu eksekusi kueri dalam analisis seberapa cepat permintaan web Anda dilayani. Informasi selengkapnya tentang ini tersedia dalam tutorial performa Application Insights, dan di halaman analitik Azure SQL.

Memeriksa rencana eksekusi kueri

Setelah Anda menentukan kueri bermasalah yang memerlukan pengoptimalan, langkah berikutnya biasanya menganalisis rencana eksekusi kueri. Ketika database menerima pernyataan SQL, mereka biasanya menghasilkan rencana tentang bagaimana rencana itu akan dijalankan; ini terkadang memerlukan pengambilan keputusan yang rumit berdasarkan indeks mana yang telah ditentukan, berapa banyak data yang ada dalam tabel, dll. (kebetulan, rencana itu sendiri biasanya harus di-cache di server untuk performa optimal). Database relasional biasanya menyediakan cara bagi pengguna untuk melihat rencana kueri, bersama dengan biaya terhitung untuk berbagai bagian kueri; ini sangat berharga untuk meningkatkan kueri Anda.

Untuk memulai di SQL Server, lihat dokumentasi tentang rencana eksekusi kueri. Alur kerja analisis umum adalah menggunakan SQL Server Management Studio, menempelkan SQL kueri lambat yang diidentifikasi melalui salah satu cara di atas, dan menghasilkan rencana eksekusi grafis:

Menampilkan rencana eksekusi SQL Server

Meskipun rencana eksekusi mungkin tampak rumit pada awalnya, ada baiknya menghabiskan sedikit waktu untuk membiasakan diri dengan mereka. Sangat penting untuk mencatat biaya yang terkait dengan setiap simpul rencana, dan untuk mengidentifikasi bagaimana indeks digunakan (atau tidak) di berbagai simpul.

Meskipun informasi di atas khusus untuk SQL Server, database lain biasanya menyediakan jenis alat yang sama dengan visualisasi serupa.

Penting

Database terkadang menghasilkan rencana kueri yang berbeda tergantung pada data aktual dalam database. Misalnya, jika tabel hanya berisi beberapa baris, database dapat memilih untuk tidak menggunakan indeks pada tabel tersebut, tetapi untuk melakukan pemindaian tabel lengkap sebagai gantinya. Jika menganalisis rencana kueri pada database pengujian, selalu pastikan paket tersebut berisi data yang mirip dengan sistem produksi Anda.

Metrik

Bagian di atas berfokus pada cara mendapatkan informasi tentang perintah Anda, dan bagaimana perintah ini dijalankan dalam database. Selain itu, EF memaparkan serangkaian metrik yang memberikan informasi tingkat yang lebih rendah tentang apa yang terjadi di dalam EF itu sendiri, dan bagaimana aplikasi Anda menggunakannya. Metrik ini dapat sangat berguna untuk mendiagnosis masalah performa dan anomali performa tertentu, seperti masalah penembolokan kueri yang menyebabkan kompilasi ulang konstan, kebocoran DbContext yang tidak diposisikan, dan lainnya.

Lihat halaman khusus pada metrik EF untuk informasi selengkapnya.

Tolok ukur dengan EF Core

Pada akhir hari, Anda terkadang perlu mengetahui apakah cara tertentu untuk menulis atau menjalankan kueri lebih cepat daripada yang lain. Penting untuk tidak pernah mengasumsikan atau berspekulasi jawabannya, dan sangat mudah untuk menyusun tolok ukur cepat untuk mendapatkan jawabannya. Saat menulis tolok ukur, sangat disarankan untuk menggunakan pustaka BenchmarkDotNet terkenal, yang menangani banyak jebakan yang ditemui pengguna saat mencoba menulis tolok ukur mereka sendiri: apakah Anda melakukan beberapa iterasi pemanasan? Berapa banyak iterasi yang benar-benar dijalankan oleh tolok ukur Anda, dan mengapa? Mari kita lihat seperti apa tolok ukur dengan EF Core.

Tip

Proyek tolok ukur lengkap untuk sumber di bawah ini tersedia di sini. Anda dianjurkan untuk menyalinnya dan menggunakannya sebagai templat untuk tolok ukur Anda sendiri.

Sebagai skenario tolok ukur sederhana, mari kita bandingkan metode berbeda berikut untuk menghitung peringkat rata-rata semua Blog dalam database kami:

  • Muat semua entitas, jumlahkan peringkat individual mereka, dan hitung rata-rata.
  • Sama seperti di atas, hanya gunakan kueri non-pelacakan. Ini harus lebih cepat, karena resolusi identitas tidak dilakukan, dan entitas tidak direkam jepret untuk tujuan pelacakan perubahan.
  • Hindari memuat seluruh instans entitas Blog sama sekali, dengan memproyeksikan peringkat saja. Menyelamatkan kita dari mentransfer kolom lain yang tidak dibutuhkan dari jenis entitas Blog.
  • Hitung rata-rata dalam database dengan menjadikannya bagian dari kueri. Ini harus menjadi cara tercepat, karena semuanya dihitung dalam database dan hanya hasilnya yang ditransfer kembali ke klien.

Dengan BenchmarkDotNet, Anda menulis kode untuk ditolok ukur sebagai metode sederhana - sama seperti pengujian unit - dan BenchmarkDotNet secara otomatis menjalankan setiap metode untuk jumlah iterasi yang memadai, dengan andal mengukur berapa lama waktu yang dibutuhkan dan berapa banyak memori yang dialokasikan. Berikut adalah metode yang berbeda (kode tolok ukur lengkap dapat dilihat di sini):

[Benchmark]
public double LoadEntities()
{
    var sum = 0;
    var count = 0;
    using var ctx = new BloggingContext();
    foreach (var blog in ctx.Blogs)
    {
        sum += blog.Rating;
        count++;
    }

    return (double)sum / count;
}

Hasilnya di bawah ini, seperti yang dicetak oleh BenchmarkDotNet:

Metode Rata-rata Kesalahan StdDev Median Rasio RasioSD Gen 0 Gen 1 Gen 2 Dialokasikan
LoadEntities 2.860,4 kami 54.31 kami 93.68 kami 2.844,5 kami 4.55 0.33 210.9375 70.3125 - 1309,56 KB
LoadEntitiesNoTracking 1,353.0 kami 21.26 kami 18.85 kami 1,355.6 kami 2.10 0.14 87.8906 3.9063 - 540,09 KB
ProjectOnlyRanking 910.9 kami 20.91 kami 61.65 kami 892.9 kami 1,46 0.14 41.0156 0.9766 - 252,08 KB
CalculateInDatabase 627.1 kami 14.58 kami 42.54 kami 626.4 kami 1 0.00 4.8828 - - 33,27 KB

Catatan

Karena metode membuat instans dan membuang konteks dalam metode , operasi ini dihitung untuk tolok ukur, meskipun secara ketat berbicara mereka bukan bagian dari proses kueri. Ini tidak masalah jika tujuannya adalah membandingkan dua alternatif satu sama lain (karena instansiasi dan pembuangan konteks sama), dan memberikan pengukuran yang lebih holistik untuk seluruh operasi.

Salah satu batasan BenchmarkDotNet adalah mengukur performa utas tunggal sederhana dari metode yang Anda berikan, dan oleh karena itu tidak cocok untuk tolok ukur skenario bersamaan.

Penting

Selalu pastikan untuk memiliki data dalam database Anda yang mirip dengan data produksi saat melakukan tolok ukur, jika tidak, hasil tolok ukur mungkin tidak mewakili performa aktual dalam produksi.