EF Core (Entity Framework Core) performansını iyileştirmenin ilk kuralı ölçmeden optimize etmemektir: önce yavaş sorguyu loglardan ve veritabanının yürütme planından bulursunuz, sonra çözüm ararsınız. Resmî belgelerde öne çıkan iyileştirmeler şunlardır: doğru indeksler, yalnızca ihtiyaç duyulan kolonları projeksiyonla almak, sonuç sayısını sınırlamak, N+1 sorgularından ve kartezyen patlamadan kaçınmak, salt okunur sorgularda change tracking'i kapatmak, toplu güncellemelerde ExecuteUpdateAsync kullanmak ve async API'leri tercih etmek.

Bu yazı bir genel bakış niteliğindedir: her başlığın ne zaman önemli olduğunu ve belgelerin verdiği örnekleri anlatır, ayrıntıları ilgili yazılara bırakır. Bağlam havuzu, derlenmiş sorgular ve sorgu önbelleği gibi ileri konular DbContext pooling ve compiled query, sayfalama EF Core sayfalama, SQL loglamanın ayrıntıları EF Core SQL loglama yazısında ele alınıyor.

Veri erişimi performansının dört boyutu

Veri erişimi performansının dört boyutu: Saf veritabanı performansı, Ağ üzerinden veri aktarımı, Ağ gidiş dönüşleri, EF çalışma zamanı yükü

Belgeler veri erişimi performansını dört geniş kategoriye ayırır. Bu ayrım, bir sorunun nereden kaynaklandığını düşünürken iyi bir kontrol listesidir.

  • Saf veritabanı performansı: EF Core LINQ sorgularınızı SQL ifadelerine çevirir ve bu ifadeler veritabanında daha verimli ya da daha verimsiz çalışabilir. Doğru yerde doğru indeks büyük fark yaratabilir; LINQ sorgusunu yeniden yazmak da EF Core'un daha iyi bir SQL üretmesini sağlayabilir.
  • Ağ üzerinden veri aktarımı: Gidip gelen veri miktarını sınırlamak önemlidir. Bu, yalnızca gerçekten ihtiyaç duyduğunuz veriyi yüklemeyi ve ilişkili entity'leri yüklerken kartezyen patlamadan kaçınmayı kapsar.
  • Ağ gidiş dönüşleri: Bir sorgunun veritabanında çalışma süresi, paketlerin uygulama ile veritabanı arasında gidip gelme süresinin gölgesinde kalabilir. Veritabanı sunucusu ne kadar uzaktaysa her gidiş dönüş o kadar pahalıdır; çok sayıda gidiş dönüş yapan "geveze" uygulamalar yavaşlar.
  • EF çalışma zamanı yükü: EF Core sorguları LINQ'ten SQL'e derler (normalde yalnızca bir kez), change tracking ek yük getirir (kapatılabilir). Belgelere göre gerçek uygulamalarda bu yük çoğu zaman ihmal edilebilir düzeydedir, çünkü toplam süreye veritabanındaki çalışma ve ağ gecikmesi hâkimdir.

Kendi sunucunuzda çalışan bir uygulamada uygulama ile veritabanı aynı makinede ya da aynı ağda olabilir; bu, gidiş dönüş maliyetini düşürür ama ortadan kaldırmaz. Uygulamayı ve veritabanını farklı sunuculara ayırdığınızda gidiş dönüş sayısı daha belirleyici hale gelir.

Önce ölçün: yavaş sorguyu bulmak

Belgeler performans konusuna Donald Knuth'un erken optimizasyonla ilgili ünlü sözünü hatırlatarak başlar: bir sorun olduğunu gösteren veri olmadan optimizasyona girişmeyin. Genel, herkese açık benchmark'ların sizin durumunuza olduğu gibi uygulanacağını varsaymamanız da vurgulanır; veritabanı gecikmesi, sorgu karmaşıklığı ve tablolardaki gerçek veri miktarı hangi çözümün en iyi olduğunu derinden etkiler.

Komut sürelerini loglamak

EF Core, ilişkisel veritabanlarında sonuçta SQL komutları çalıştırır. Bir sorgu çok uzun sürüyorsa, örneğin bir indeks eksikse, bunu komut çalıştırma loglarına bakarak görebilirsiniz. Basit loglamayı Information seviyesinde açmak yeterlidir:

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

Bu seviyede EF Core her komut çalıştırması için süreyi de içeren bir log mesajı üretir. Belgelerdeki örnek çıktı şöyledir:

text
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'

Örnekteki komut 4 milisaniye sürmüştür. Bir komut beklenenden uzun sürüyorsa olası bir suçlu bulmuşsunuz demektir. Komut logları beklenmeyen gidiş dönüşleri de ortaya çıkarır: tek bir komut beklediğiniz yerde birden fazla komut görürsünüz. Komutları kaynak LINQ sorgularıyla eşleştirmek için TagWith ile sorgulara etiket ekleyebilirsiniz; etiket SQL'e bir yorum satırı olarak girer.

Yürütme planı ve veritabanı araçları

Sorunlu sorguyu bulduktan sonraki adım genellikle sorgunun yürütme planını incelemektir. Veritabanları bir SQL ifadesi aldığında onu nasıl çalıştıracaklarına dair bir plan üretir; bu plan hangi indekslerin tanımlı olduğuna ve tablolarda ne kadar veri bulunduğuna göre karmaşık kararlar içerebilir. İlişkisel veritabanları planı, parçaların maliyetleriyle birlikte görmenin bir yolunu sunar. SQL Server için belgeler SQL Server Management Studio ile grafiksel yürütme planı üretmeyi, Activity Monitor ve Extended Events özelliklerini anar; diğer veritabanları da benzer araçlar sağlar. PostgreSQL tarafında EXPLAIN kullanımı PostgreSQL performans optimizasyonu yazısında anlatılıyor.

Belgelerin bu konudaki önemli uyarısı şudur: veritabanları gerçek veriye göre farklı planlar üretebilir. Örneğin tabloda yalnızca birkaç satır varsa veritabanı indeksi kullanmak yerine tüm tabloyu taramayı seçebilir. Planları bir test veritabanında inceliyorsanız verinin üretimdekine benzer olduğundan emin olun.

Metrikler ve benchmark

EF Core 9.0 ile birlikte System.Diagnostics.Metrics üzerinden metrikler yayımlanır: etkin bağlam sayısı, çalıştırılan sorgular, derlenmiş sorgu önbelleğindeki isabet ve ıskalar, iyimser eşzamanlılık hataları gibi. Bunlar sürekli yeniden derleme yapan sorgular ya da dispose edilmeyen bağlamlar gibi sorunları teşhis etmeye yarar; ayrıntılar EF Core loglama ve metrikler yazısında. İki yazım biçiminden hangisinin daha hızlı olduğunu bilmek istediğinizde ise belgeler tahmin yürütmek yerine BenchmarkDotNet ile hızlıca bir benchmark yazmayı önerir; benchmark verisinin de üretimdekine benzer olması gerekir.

Belgelerdeki örnek benchmark, blogların ortalama puanını dört farklı yolla hesaplar: bütün entity'leri yükleyip toplamak, aynısını takipsiz sorguyla yapmak, yalnızca puan kolonunu projeksiyonla almak ve ortalamayı veritabanında AverageAsync ile hesaplamak. Belgelerdeki ölçümde en hızlı ve en az bellek ayıran yöntem hesaplamayı veritabanında yapan sürüm, en yavaşı ise bütün entity'leri takipli yükleyen sürüm çıkmıştır; takipsiz sorgu ve projeksiyon ikisinin arasında kalmıştır. Bu sıralama aşağıdaki başlıkların neden önemli olduğunu özetler.

İndeksleri doğru kullanmak

Belgelere göre bir sorgunun hızlı çalışıp çalışmadığını belirleyen ana etken, uygun yerlerde indeksleri doğru kullanıp kullanmadığıdır. İndeks sorunlarını fark etmek kolay değildir, çünkü bir sorgunun indeks kullanıp kullanmayacağı her zaman açık değildir:

csharp
// Basa gore eslesir, bu yuzden (SQL Server'da) indeksi kullanir
var posts1 = await context.Posts.Where(p => p.Title.StartsWith("A")).ToListAsync();
// Sona gore eslesir, indeksi kullanmaz
var posts2 = await context.Posts.Where(p => p.Title.EndsWith("A")).ToListAsync();

Belgeler indekslerle ilgili özel bir EF bilgisi gerekmediğini, genel veritabanı bilgisinin EF uygulamaları için de aynen geçerli olduğunu söyler ve şu ilkeleri hatırlatır:

  • İndeksler sorguları hızlandırır ama güncel tutulmaları gerektiği için güncellemeleri yavaşlatır. Gereksiz indeks tanımlamayın; indeksi satırların bir alt kümesiyle sınırlamak için filtreli indeksleri düşünün.
  • Bileşik indeksler birden fazla kolona göre filtreleyen sorguları hızlandırır; sıralamaya bağlı olarak tüm kolonlara göre filtrelemeyen sorguları da hızlandırabilir. A ve B kolonları üzerindeki bir indeks, A ve B'ye ya da yalnızca A'ya göre filtreyi hızlandırır, yalnızca B'ye göre filtreyi hızlandırmaz.
  • Bir kolon üzerindeki bir ifadeye göre (örneğin price / 2) filtreleyen sorgular basit bir indeksi kullanamaz. Bu ifade için kalıcı (stored) bir hesaplanmış kolon tanımlayıp onun üzerine indeks oluşturabilirsiniz; bazı veritabanları ifade indekslerini de destekler.
  • Veritabanları indeksleri farklı biçimlerde yapılandırmaya izin verir; SQL Server sağlayıcısı örneğin clustered indeks ve fill factor ayarlarını Fluent API üzerinden sunar.

İndeks tanımlama EF Core index ve unique index, hesaplanmış kolonlar default value ve computed column yazısında anlatılıyor.

Yalnızca gereken kolonları almak

Entity örneklerini sorgulamak kolaydır, ama çoğu zaman gerekenden fazla veri getirir. Belgelerdeki örnekte yalnızca her blogun Url değeri yazdırılır, ancak sorgu bütün entity'yi ister:

csharp
await foreach (var blog in context.Blogs.AsAsyncEnumerable())
{
    Console.WriteLine("Blog: " + blog.Url);
}

Belgelerin örnek çıktısında bu sorgu BlogId, CreationDate, Name, Rating ve Url kolonlarının hepsini çeker. Select ile hangi kolonların gerektiğini söylediğinizde üretilen SQL yalnızca SELECT [b].[Url] olur:

csharp
await foreach (var blogName in context.Blogs.Select(b => b.Url).AsAsyncEnumerable())
{
    Console.WriteLine("Blog: " + blogName);
}

Birden fazla kolon gerekiyorsa istediğiniz özelliklerle bir anonim tipe projeksiyon yapın. Bu teknik salt okunur sorgular için çok yararlıdır; ancak alınan verileri güncellemeniz gerekiyorsa işler karmaşıklaşır, çünkü EF Core'un change tracking mekanizması yalnızca entity örnekleriyle çalışır.

Sonuç sayısını sınırlamak

Varsayılan olarak bir sorgu filtreyle eşleşen bütün satırları döndürür ve bunun ne kadar veri olacağı veritabanındaki gerçek veriye bağlıdır. Test veritabanları az veri içerdiği için sorun testte görünmez, üretimde ortaya çıkar. En azından bir Take ile üst sınır koymak, tam bir çözüm olarak da sayfalama uygulamak gerekir. Skip ve Take ile yapılan ofset sayfalaması sezgiseldir ama verimsizdir; yalnızca sonraki ve önceki sayfa gezintisi gerekiyorsa belgeler keyset sayfalamayı önerir. Ayrıntılar EF Core sayfalama yazısında.

İlişkili verileri yüklerken iki tuzak

İlişkili verileri yüklerken iki tuzak: Kartezyen patlama, N+1 sorunu ve lazy loading

Kartezyen patlama

İlişkisel veritabanlarında ilişkili entity'ler tek sorguda JOIN eklenerek yüklenir. Tipik bir blogun çok sayıda yazısı varsa her yazı satırı blogun bilgilerini tekrar eder. Belgeler bu tekrarın yol açtığı soruna "kartezyen patlama" der; birden fazla bire çok ilişki yüklendikçe tekrar eden veri miktarı büyüyebilir. EF Core bunu ilişkili entity'leri ayrı sorgularla yükleyen split query ile önlemenize izin verir; belgeler mevcut uygulamada her sorgu için ayrı bir gidiş dönüş yapıldığını da not eder. Konu split query ve AsSplitQuery yazısında.

N+1 sorunu ve lazy loading

İlişkili verileri yüklerken genellikle ne gerektiğini önceden bilirsiniz; bu durumda belgelere göre eager loading her zaman daha iyidir, çünkü EF Core gereken her şeyi tek gidiş dönüşte getirebilir. Filtered include ile yüklenecek ilişkili kayıtları da sınırlayabilirsiniz. Lazy loading ise kolay görünse de gereksiz ek gidiş dönüşlere çok yatkındır. Belgelerdeki örnekte her blogun yazıları bir iç döngüde dolaşılır ve loglar her blog için ayrı bir sorgu gönderildiğini gösterir (belgelerdeki örnek çıktının kısaltılmış hali):

text
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT [b].[BlogId], [b].[Rating], [b].[Url]
      FROM [Blogs] AS [b]
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (5ms) [Parameters=[@__p_0='1'], CommandType='Text', CommandTimeout='30']
      SELECT [p].[PostId], [p].[BlogId], [p].[Content], [p].[Title]
      FROM [Post] AS [p]
      WHERE [p].[BlogId] = @__p_0

... and so on

Önce bütün blogları getiren bir sorgu, ardından her blog için yazılarını getiren ayrı bir sorgu çalışır; buna N+1 sorunu denir ve çok ciddi performans sorunlarına yol açabilir. Belgelerin çözümü, yalnızca gereken verileri tek sorguda alan bir projeksiyondur:

csharp
await foreach (var blog in context.Blogs.Select(b => new { b.Url, b.Posts }).AsAsyncEnumerable())
{
    foreach (var post in blog.Posts)
    {
        Console.WriteLine($"Blog {blog.Url}, Post: {post.Title}");
    }
}

Belgeler, lazy loading N+1 sorununu kazara tetiklemeyi çok kolaylaştırdığı için ondan kaçınmayı önerir; eager ve explicit loading kaynak kodda bir gidiş dönüşün nerede olduğunu açıkça gösterir. Ayrıntılar Include ve eager loading ile lazy loading yazılarında.

Tamponlama ve akış

Tamponlama bütün sorgu sonuçlarını belleğe yüklemek, akış (streaming) ise sonuçları uygulamaya teker teker vermek demektir. İlke olarak akış yapan bir sorgunun bellek ihtiyacı sabittir; tamponlayan bir sorgu ise satır sayısı arttıkça daha fazla bellek ister. ToListAsync ve ToArrayAsync bütün sonuç kümesini tamponlar, await foreach ve AsAsyncEnumerable akış yapar. Belgeler, sonuca başka bir LINQ operatörü uygulayacaksanız ToList ya da ToArray yerine AsEnumerable kullanmanızı önerir; aksi halde bütün sonuçlar gereksiz yere belleğe alınır.

EF Core bazı durumlarda sonuçları kendisi de içeride tamponlar: yeniden deneyen bir execution strategy etkin olduğunda (sorgu yeniden denenirse aynı sonuçları döndürebilmek için) ve split query kullanıldığında (SQL Server'da MARS etkin değilse son sorgu dışındaki sorguların sonuçları). Bu iç tamponlama sizin yaptığınız tamponlamaya eklenir; örneğin yeniden deneme stratejisi etkinken ToList kullanırsanız sonuç kümesi belleğe iki kez yüklenir. Yeniden deneme stratejileri EnableRetryOnFailure yazısında anlatılıyor.

Takipli ve takipsiz sorgular

EF Core varsayılan olarak entity örneklerini takip eder ve bunun iki maliyeti vardır. Birincisi, takip edilen örnekler için bir sözlük tutulur ve yeni veri yüklendiğinde aynı anahtarlı bir örnek olup olmadığına bakılır (identity resolution). İkincisi, her yüklenen örneğin bir anlık görüntüsü (snapshot) alınır ve SaveChanges sırasında değişiklikleri bulmak için bu görüntüyle karşılaştırılır. Değişikliklerin kaydedilmediği salt okunur senaryolarda takipsiz sorgularla bu yükten kurtulabilirsiniz. Belgelerdeki 10 blog ve blog başına 20 yazı yükleyen benchmark'ta takipsiz sorgu, takipli sorgunun yaklaşık yüzde 71'i kadar sürede tamamlanmıştır. Dikkat edilmesi gereken nokta şudur: takipsiz sorgular identity resolution yapmadığı için birden çok yazının başvurduğu aynı blog, her biri için ayrı bir örnek olarak oluşturulur. Ayrıntılar AsNoTracking ve tracking sorguları yazısında.

Ham SQL, fonksiyonlar ve view'lar

Bazen sorgunuz için EF Core'un üretmediği daha iyi bir SQL vardır; bu, veritabanınıza özgü ve desteklenmeyen bir yapı olabilir ya da EF Core henüz o çeviriyi yapmıyor olabilir. Belgeler üç yol sayar: sorguda doğrudan SQL kullanmak (ve üzerine LINQ ile sorgu eklemek), bir kullanıcı tanımlı fonksiyon tanımlayıp sorgulardan çağırmak ya da bir veritabanı view'ı tanımlayıp ondan sorgulamak. Belgelere göre ham SQL, EF Core'un istediğiniz SQL'i üretemeyeceğinden emin olduktan sonra ve performansın bunu haklı çıkaracak kadar önemli olduğu durumlarda son çare olarak kullanılmalıdır, çünkü önemli bakım dezavantajları getirir. Ayrıntılar FromSql ile ham SQL ve client evaluation yazılarında.

Async programlama

Belgelere göre uygulamanızın ölçeklenebilmesi için senkron API'ler yerine her zaman async API'leri kullanmak önemlidir; örneğin SaveChanges yerine SaveChangesAsync. Senkron API'ler veritabanı G/Ç süresi boyunca iş parçacığını bloke eder, bu da daha fazla iş parçacığı ve daha fazla bağlam değişimi ihtiyacı doğurur. Aynı uygulamada senkron ve async kodu karıştırmaktan da kaçınmanız gerekir; bu, ince thread pool tükenmesi sorunlarını kolayca tetikler. Belgeler ayrıca Microsoft.Data.SqlClient'ın async uygulamasında bilinen bazı sorunlar olduğunu, beklenmeyen performans sorunları görürseniz özellikle büyük metin veya ikili değerlerde senkron komut çalıştırmayı denemeyi önerir. Konu EF Core async yazısında.

Verimli güncelleme

Verimli güncelleme: Batching, ExecuteUpdate ve ExecuteDelete

Batching

EF Core, SaveChanges çağrıldığında biriktirdiği değişiklikleri tek tek göndermek yerine gruplayarak (batching) gidiş dönüş sayısını azaltır. Bir blogu yükleyip URL'sini değiştirdiğiniz ve iki yeni blog eklediğiniz bir örnekte iki INSERT ve bir UPDATE tek gidiş dönüşte çalışır. Tek gruptaki ifade sayısı sağlayıcıya bağlıdır. Belgelere göre SQL Server için 4'ten az ifadede batching genellikle daha az verimlidir ve yararı yaklaşık 40 ifadeden sonra azalır; bu yüzden EF Core varsayılan olarak tek grupta en fazla 42 ifade çalıştırır. Bu eşikleri MinBatchSize ve MaxBatchSize ile değiştirebilirsiniz, ancak belgeler bunu dikkatli bir benchmark yapmadan önermez.

csharp
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder.UseSqlServer(
        @"Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True",
        o => o
            .MinBatchSize(1)
            .MaxBatchSize(100));
}

ExecuteUpdate ve ExecuteDelete

Bütün çalışanlara zam yapmak için çalışanları yükleyip maaşlarını artırmak ve SaveChanges çağırmak geçerli bir koddur, ama verimsizdir: bütün satırlar uygulamaya gelir, change tracking anlık görüntü alıp karşılaştırır ve her çalışan için ayrı bir UPDATE gönderilir. EF Core 7.0'dan beri aynı işi tek bir ifadeyle yapabilirsiniz:

csharp
await context.Employees.ExecuteUpdateAsync(s => s.SetProperty(e => e.Salary, e => e.Salary + 1000));

Belgelere göre bu çağrı veritabanına UPDATE [Employees] SET [Salary] = [Salary] + 1000; gönderir; hiçbir veri yüklenmez ve change tracking kullanılmaz. Ayrıntılar ve change tracker ile ilişkisi ExecuteUpdate ve ExecuteDelete yazısında.

Performans için modelleme

Model tasarımı uygulama performansını derinden etkileyebilir ve üretimdeki bir modeli değiştirmek zor olduğu için baştan düşünmeye değer. Belgeler normalleştirilmiş bir modelin iyi bir başlangıç olduğunu, ama gerçek uygulamalarda bazı pragmatik ödünlerin çok işe yarayabileceğini söyler.

  • Denormalizasyon: Sık gösterilen bir hesaplanmış değeri (örneğin blog yazılarının ortalama puanı) her seferinde JOIN ve toplama ile hesaplamak yerine ayrı bir kolonda tutmak. Bu bir önbellek biçimidir ve değeri güncel tutma sorunu getirir.
  • Kalıcı hesaplanmış kolonlar: Önbelleğe alınacak veri aynı satırdaki diğer kolonlardan türetiliyorsa veritabanı bu kolonu otomatik günceller ve üzerine indeks tanımlanabilir.
  • Girdiler değişince güncelleme: Değer başka tablolara bağlıysa SaveChanges olayları veya interceptor'lar ya da veritabanı trigger'ları ile yeniden hesaplanabilir. EF Core trigger oluşturmak için özel bir API sunmaz; boş bir migration'a ham SQL ile eklenebilir.
  • Materialized veya indexed view'lar: Bütün bir sorgu sonucunu diskte önbelleğe alır. Desteği veritabanına göre değişir; EF Core bunlar için de özel API sunmaz.

Kalıtım eşleme stratejisi de büyük fark yaratır. Belgelere göre ölçümler, TPT'nin çoğu durumda performans açısından en zayıf seçenek olduğunu gösterir; TPH tek tablodan okurken TPT birden fazla tabloyu birleştirmek zorundadır. TPC, TPH'ye benzer performans gösterir ve tek bir yaprak tipi sorgularken öne çıkar. Belgelerdeki 7 tipli ve toplam 35000 satırlık benchmark'ta TPT, TPH ve TPC'ye göre yaklaşık iki kat uzun sürmüştür; belgeler sonuçların sorguya göre değişeceğini ve kendi ölçümünüzü yapmanızı vurgular. Konu TPH, TPT ve TPC yazısında.

Veritabanının dışında önbellek

Belgelerin son tavsiyesi basittir: bir veritabanıyla etkileşmenin en verimli yolu hiç etkileşmemektir. Veritabanı erişimi bir darboğaz olarak ortaya çıkıyorsa bazı sonuçları veritabanının dışında önbelleğe almak istek sayısını azaltabilir. Önbellek karmaşıklık getirir, ama ölçeklenebilir bir uygulamanın önemli bir parçasıdır: uygulama katmanını sunucu ekleyerek ölçeklemek kolaydır, veritabanı katmanını ölçeklemek ise genellikle çok daha zordur.

Hangi belirtide nereye bakmalı?

BelirtiOlası nedenİlk bakılacak yer
Tek bir sorgu yavaşEksik veya kullanılmayan indeksKomut süresi logu ve yürütme planı
Tek istekte çok sayıda sorguLazy loading ile N+1Eager loading veya projeksiyon
Az satır ama çok veriGereksiz kolonlar, kartezyen patlamaSelect ile projeksiyon, split query
Bellek kullanımı yüksekBüyük sonuç kümesi tamponlanıyorTake, sayfalama, akış
Toplu güncelleme yavaşSatır başına UPDATEExecuteUpdateAsync
Sorgu önbellek isabeti düşükSabitlerle dinamik sorgularParametreleme, compiled query

Sık yapılan hatalar

  • Ölçmeden optimize etmek: Sorunun nerede olduğunu bilmeden yapılan değişiklikler çoğu zaman yanlış yere odaklanır. Önce loglar ve yürütme planı.
  • Az veriyle test etmek: Planlar ve benchmark'lar veri miktarına bağlıdır; üretime benzer veri olmadan yapılan ölçümler yanıltıcıdır.
  • Üretimde komut loglamasını açık unutmak: Uygulamayı yavaşlatır ve diski doldurabilir.
  • Salt okunur listelerde tam entity çekmek: Projeksiyon ve takipsiz sorgu çoğu zaman yeterlidir.
  • Senkron ve async kodu karıştırmak: Thread pool tükenmesine yol açabilir.
  • Ham SQL'e erken başvurmak: Önce LINQ sorgusunu yeniden yazmayı ve indeksleri deneyin; ham SQL bakım maliyeti getirir.

Sık Sorulan Sorular

EF Core yavaş mı?

Belgelere göre gerçek uygulamalarda EF Core'un kendi çalışma zamanı yükü çoğu durumda ihmal edilebilir düzeydedir; toplam süreye veritabanındaki çalışma ve ağ gecikmesi hâkimdir. Yavaşlık genellikle eksik indeks, gereksiz veri aktarımı ya da fazla gidiş dönüş gibi nedenlerden kaynaklanır.

Hangi sorgunun yavaş olduğunu nasıl bulurum?

LogTo ile Information seviyesinde loglamayı açıp komut sürelerine bakın, önemli sorguları TagWith ile etiketleyin, ardından şüpheli sorgunun yürütme planını veritabanı aracınızda inceleyin.

AsNoTracking her zaman kullanılmalı mı?

Yalnızca değişiklik kaydetmeyeceğiniz salt okunur sorgularda. Takipsiz sorgular identity resolution yapmadığı için aynı satır birden fazla örnek olarak oluşturulabilir; kodunuzun buna hazır olması gerekir.

DbContext pooling ne zaman devreye girer?

Belgelere göre bir DbContext genellikle hafif bir nesnedir ve çoğu uygulama onu fark edilir bir performans etkisi olmadan oluşturup dispose edebilir. Yüksek performans gerektiren senaryolarda ise her örneğin iç servisleri yeniden kurmasının maliyeti önemli hale gelebilir; pooling ve compiled query gibi ileri teknikler DbContext pooling yazısında anlatılıyor.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: EF Core öğrenme rehberi · DbContext pooling · Sayfalama · Split query · SQL loglama · PostgreSQL performans