Performans Tanılama
Bu bölümde, EF uygulamanızdaki performans sorunlarını algılamanın yolları ve sorunlu bir alan belirlendikten sonra kök sorunu tanımlamak için bunları daha fazla analiz etme adımları açıklanmıştır. Herhangi bir sonuca atlamadan önce sorunları dikkatlice tanılamak ve araştırmak ve sorunun kökünün nerede olduğunu varsaymak önemlidir.
Günlüğe kaydetme yoluyla yavaş veritabanı komutlarını tanımlama
Günün sonunda EF, veritabanınızda yürütülecek komutları hazırlar ve yürütür; ilişkisel veritabanı ile bu, sql deyimlerini ADO.NET veritabanı API'sini kullanarak yürütme anlamına gelir. Belirli bir sorgu çok fazla zaman alıyorsa (örneğin, bir dizin eksikse), komut yürütme günlükleri incelenerek ve gerçekte ne kadar sürdüğünü gözlemleyerek bu bulunabilir.
EF, basit günlüğe kaydetme veya Microsoft.Extensions.Logging aracılığıyla komut yürütme sürelerini yakalamayı çok kolaylaştırır:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True;ConnectRetryCount=0")
.LogTo(Console.WriteLine, LogLevel.Information);
}
Günlüğe kaydetme düzeyi olarak ayarlandığında LogLevel.Information
EF, her komut yürütmesi için geçen süre boyunca bir günlük iletisi gönderir:
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'
Yukarıdaki komut 4 milisaniye sürdü. Belirli bir komut beklenenden daha uzun sürüyorsa, performans sorununun olası bir suçlusunu buldunuz ve şimdi neden yavaş çalıştığını anlamak için buna odaklanabilirsiniz. Komut günlüğü beklenmeyen veritabanı gidiş dönüşlerinin yapıldığı durumları da ortaya çıkarabilir; bu, yalnızca birinin beklendiği birden çok komut olarak görünür.
Uyarı
Komut yürütme günlüğünü üretim ortamınızda etkin bırakmak genellikle kötü bir fikirdir. Günlüğün kendisi uygulamanızı yavaşlatır ve hızlı bir şekilde sunucunuzun diskini doldurabilecek büyük günlük dosyaları oluşturabilir. Uygulamanızı dikkatle izlerken veri toplamak veya üretim öncesi bir sistemde günlük verilerini yakalamak için yalnızca kısa bir süre boyunca oturum açmayı sürdürmeniz önerilir.
Veritabanı komutlarını LINQ sorgularına ilişkilendirme
Komut yürütme günlüğüyle ilgili bir sorun, SQL sorguları ile LINQ sorgularını ilişkilendirmenin bazen zor olmasıdır: EF tarafından yürütülen SQL komutları, oluşturuldukları LINQ sorgularından çok farklı görünebilir. Bu zorluğa yardımcı olmak için, EF'nin SQL sorgusuna küçük ve tanımlayıcı bir açıklama eklemenizi sağlayan sorgu etiketleri özelliğini kullanmak isteyebilirsiniz:
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();
Etiket günlüklerde gösterilir:
-- 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
Komut yürütme günlüklerini daha hemen okunabilir hale getirmek için genellikle uygulamanın ana sorgularını bu şekilde etiketlemeye değer.
Performans verilerini yakalamak için diğer arabirimler
EF'in komut yürütme sürelerini yakalamak için günlüğe kaydetme özelliğinin çeşitli alternatifleri vardır ve bu daha güçlü olabilir. Veritabanları genellikle basit yürütme sürelerinin ötesinde çok daha zengin, veritabanına özgü bilgiler sağlayan kendi izleme ve performans analizi araçlarıyla birlikte gelir; gerçek kurulum, yetenekler ve kullanım veritabanları arasında önemli ölçüde farklılık gösterir.
Örneğin, SQL Server Management Studio, SQL Server örneğinize bağlanabilen ve değerli yönetim ve performans bilgileri sağlayabilecek güçlü bir istemcidir. Ayrıntılara girmek bu bölümün kapsamının dışındadır, ancak bahsetmeye değer iki özellik, sunucu etkinliğinin canlı bir panosunu (en pahalı sorgular dahil) sağlayan Etkinlik İzleyicisi ve tam gereksinimlerinize göre uyarlanabilecek rastgele veri yakalama oturumları tanımlamaya olanak tanıyan Genişletilmiş Olaylar (XEvent) özelliğidir. İzlemeye ilişkin SQL Server belgeleri, bu özellikler ve diğerleri hakkında daha fazla bilgi sağlar.
Performans verilerini yakalamaya yönelik bir diğer yaklaşım da EF veya veritabanı sürücüsü tarafından arabirim aracılığıyla DiagnosticSource
otomatik olarak yayılan bilgileri toplamak ve ardından bu verileri analiz etmek veya bir panoda görüntülemektir. Azure kullanıyorsanız Azure Uygulaması lication Insights, web isteklerinizin ne kadar hızlı sunulduğuna ilişkin analizde veritabanı performansını ve sorgu yürütme sürelerini tümleştirerek bu kadar güçlü bir izleme sağlar. Bu konuda daha fazla bilgiye Application Insights performans öğreticisinde ve Azure SQL analizi sayfasından ulaşabilirsiniz.
Sorgu yürütme planlarını inceleme
İyileştirme gerektiren sorunlu bir sorguyu sabitledikten sonra, sonraki adım genellikle sorgunun yürütme planını analiz etmektir. Veritabanları bir SQL deyimi aldığında, genellikle bu planın nasıl yürütüldüğünü gösteren bir plan oluştururlar; Bu bazen hangi dizinlerin tanımlandığına, tablolarda ne kadar veri bulunduğuna vb. bağlı olarak karmaşık karar alma gerektirir (bu arada, planın kendisi genellikle en iyi performans için sunucuda önbelleğe alınmalıdır). İlişkisel veritabanları genellikle kullanıcıların sorgu planını görmeleri için bir yol sağlar ve sorgunun farklı bölümleri için hesaplanan maliyetlendirme sağlar; bu, sorgularınızı geliştirmek için çok değerlidir.
SQL Server'ı kullanmaya başlamak için sorgu yürütme planlarına ilişkin belgelere bakın. Tipik analiz iş akışı, SQL Server Management Studio'yu kullanmak, yukarıdaki araçlardan biri aracılığıyla tanımlanan yavaş bir sorgunun SQL'ini yapıştırmak ve grafik yürütme planı oluşturmaktır:
Yürütme planları ilk başta karmaşık görünse de, bunları tanımak için biraz zaman harcamaya değer. Özellikle planın her düğümüyle ilişkili maliyetleri not almak ve dizinlerin çeşitli düğümlerde nasıl kullanıldığını (veya kullanılmadığını) belirlemek önemlidir.
Yukarıdaki bilgiler SQL Server'a özgü olsa da, diğer veritabanları genellikle benzer görselleştirmelere sahip aynı türde araçlar sağlar.
Önemli
Veritabanları bazen veritabanındaki gerçek verilere bağlı olarak farklı sorgu planları oluşturur. Örneğin, bir tablo yalnızca birkaç satır içeriyorsa, veritabanı bu tablodaki bir dizini kullanmayı değil, bunun yerine tam tablo taraması yapmayı seçebilir. Test veritabanında sorgu planlarını analiz ediyorsanız, her zaman üretim sisteminize benzer veriler içerdiğine emin olun.
Ölçümler
Yukarıdaki bölümlerde komutlarınız hakkında bilgi edinme ve bu komutların veritabanında nasıl yürütülür odaklanmıştır. Buna ek olarak EF, EF'nin içinde neler olduğu ve uygulamanızın bunu nasıl kullandığı hakkında daha düşük düzeyde bilgi sağlayan bir ölçüm kümesini kullanıma sunar. Bu ölçümler, sürekli yeniden derlemeye neden olan sorgu önbelleğe alma sorunları, dağıtılmamış DbContext sızıntıları ve diğerleri gibi belirli performans sorunlarını ve performans anomalilerini tanılamak için çok yararlı olabilir.
Daha fazla bilgi için EF'in ölçümlerindeki ayrılmış sayfaya bakın.
EF Core ile karşılaştırma
Günün sonunda, bazen sorgu yazmanın veya yürütmenin belirli bir yolunun başka bir sorgudan daha hızlı olup olmadığını bilmeniz gerekir. Yanıtı asla varsaymamak veya tahmin etmemek önemlidir ve yanıtı almak için hızlı bir karşılaştırma yapmak son derece kolaydır. Kıyaslamalar yazarken, kullanıcıların kendi karşılaştırmalarını yazmaya çalışırken karşılaştığı birçok tuzakla başa çıkan iyi bilinen BenchmarkDotNet kitaplığını kullanmanız kesinlikle önerilir: Bazı ısınma yinelemeleri gerçekleştirdiniz mi? Karşılaştırmanız gerçekte kaç yineleme çalıştırıyor ve neden? ŞIMDI EF Core ile karşılaştırmanın nasıl göründüğüne göz atalım.
İpucu
Aşağıdaki kaynak için tam karşılaştırma projesine buradan ulaşabilirsiniz. Bunu kopyalamanız ve kendi karşılaştırmalarınız için şablon olarak kullanmanız tavsiye edilir.
Basit bir karşılaştırma senaryosu olarak veritabanımızdaki tüm Blogların ortalama derecelendirmesini hesaplamaya yönelik aşağıdaki farklı yöntemleri karşılaştıralım:
- Tüm varlıkları yükleyin, tek tek derecelendirmelerini toplayın ve ortalamayı hesaplayın.
- Yukarıdakiyle aynı şekilde, yalnızca izleme olmayan bir sorgu kullanın. Kimlik çözümlemesi gerçekleştirilmediğinden ve varlıklar değişiklik izleme amacıyla anlık görüntülenmediğinden bu daha hızlı olmalıdır.
- Yalnızca derecelendirmeyi yansıtarak Blog varlık örneklerinin tamamını yüklemekten kaçının. , Blog varlık türünün diğer, gereksiz sütunlarını aktarmamızı engeller.
- Veritabanındaki ortalamayı sorgunun bir parçası yaparak hesaplayın. Her şey veritabanında hesaplandığından ve yalnızca sonuç istemciye aktarıldığı için bu en hızlı yol olmalıdır.
BenchmarkDotNet ile, aynı birim testi gibi basit bir yöntem olarak karşılaştırma yapılacak kodu yazarsınız ve BenchmarkDotNet her yöntemi otomatik olarak yeterli sayıda yineleme için çalıştırır ve ne kadar süre aldığını ve ne kadar bellek ayrıldığını güvenilir bir şekilde ölçer. Farklı yöntemler aşağıdadır (karşılaştırma kodunun tamamı burada görülebilir):
- Varlıkları yükleme
- Varlıkları yükle, izleme yok
- Yalnızca proje derecelendirmesi
- Veritabanında hesaplama
[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;
}
BenchmarkDotNet tarafından yazdırıldığı gibi sonuçlar aşağıdadır:
Metot | Ortalama | Hata | StdDev | Ortanca | Oran | RatioSD | 0. Nesil | 1. Nesil | 2. Nesil | Tahsis edilen |
---|---|---|---|---|---|---|---|---|---|---|
LoadEntities | 2.860.4 bize | 54.31 bize | 93.68 bize | 2,844,5 biz | 4,55 | 0.33 | 210.9375 | 70.3125 | - | 1309,56 KB |
LoadEntitiesNoTracking | 1,353.0 bize | 21.26 bize | 18.85 bize | 1.355.6 bize | 2.10 | 0,14 | 87.8906 | 3.9063 | - | 540,09 KB |
ProjectOnlyRanking | 910.9 bize | 20.91 bize | 61.65 bize | 892.9 bize | 1,46 | 0,14 | 41.0156 | 0.9766 | - | 252,08 KB |
CalculateInDatabase | 627.1 biz | 14.58 biz | 42.54 bize | 626.4 bize | 1,00 | 0,00 | 4.8828 | - | - | 33,27 KB |
Not
Yöntemler bağlamın örneğini oluşturup yöntemin içinde attıkça, bu işlemler karşılaştırma için sayılır, ancak kesin olarak ifade etmek gerekirse sorgulama işleminin bir parçası değildir. Amacın iki alternatifi birbiriyle karşılaştırmak (bağlam örneği oluşturma ve atma aynı olduğundan) ve tüm işlem için daha bütünsel bir ölçüm sağlamak olup olmadığı önemli değildir.
BenchmarkDotNet'in bir sınırlaması, sağladığınız yöntemlerin basit, tek iş parçacıklı performansını ölçüp eş zamanlı senaryoları karşılaştırmak için uygun olmamasıdır.
Önemli
Karşılaştırma yaparken veritabanınızda her zaman üretim verilerine benzer veriler olduğundan emin olun; aksi takdirde karşılaştırma sonuçları üretimdeki gerçek performansı göstermeyebilir.