"Hangi ORM kullanmalıyım" sorusu, genel olarak veri erişim stratejisi ve büyük ölçekli bir uygulamada performans optimizasyonu söz konusu olduğunda, büyük bir buzdağının tepesini hedef almaktadır.
Veri Tabanı Tasarımı ve Bakımı
Bu, geniş bir aralıkta, veri odaklı bir uygulamanın veya web sitesinin verimliliğinin en önemli belirleyicisidir ve genellikle programcılar tarafından tamamen göz ardı edilir.
Uygun normalleştirme teknikleri kullanmazsanız, siteniz mahkumdur. Birincil anahtarlarınız yoksa hemen hemen her sorgu köpek yavaş olacaktır. Anahtar-Değer Çiftleri (AKA Varlık-Nitelik-Değer) tablolarını iyi bir sebep olmadan kullanmak gibi iyi bilinen kalıpları kullanıyorsanız, fiziksel okuma ve yazma sayısını patlatacaksınız.
Sayfa sıkıştırma, FILESTREAM
depolama (ikili veriler için), hiyerarşiler için SPARSE
sütunlar , vb. Gibi veritabanının size sunduğu özelliklerden yararlanmazsanız hierarchyid
(tüm SQL Server örnekleri) performans olabilir göreceğim.
Veritabanınızı tasarladıktan ve en azından şimdilik mümkün olabileceği kadar iyi olduğuna ikna olduktan sonra , veri erişim stratejiniz hakkında endişelenmeye başlamalısınız .
İstekli vs tembel yükleme
Çoğu ORM , ilişkiler için tembel yükleme denilen bir teknik kullandı ; bu, varsayılan olarak bir kerede bir varlık (tablo sırası) yükleyeceği ve bir veya daha fazla ilgili (yabancı) yüklemesi gerektiğinde veritabanına bir gidiş-dönüş yapacak anlamına gelir. key) satırlar.
Bu iyi ya da kötü bir şey değil, verilerle gerçekte ne yapılması gerektiğine ve ne kadar önceden bildiğinize bağlı. Bazen tembel yükleme kesinlikle yapılacak en doğru şeydir. Örneğin, NHibernate, hiçbir şey için sorgulama yapmamaya ve belirli bir kimlik için bir proxy oluşturmaya karar verebilir . İhtiyacınız olan tek şey kimliğin kendisi ise neden daha fazlasını isteyelim? Öte yandan, her bir öğenin bir ağacını 3 seviyeli bir hiyerarşide basmaya çalışıyorsanız, tembel yükleme, performans için aşırı derecede kötü olan bir O (N²) işlemi olur .
"Saf SQL" (örneğin, ham ADO.NET sorguları / saklı yordamları) kullanmanın ilginç yararlarından biri, belirli bir ekranı veya sayfayı görüntülemek için tam olarak hangi verilerin gerekli olduğunu düşünmeye zorlamasıdır. ORMS'nin ve tembel yükleme özellikleri yok önlemek Bunu yaparken sizi, ancak do size, iyi ... olma fırsatını sunmaktadır tembel ve yanlışlıkla yürütmek sorguların sayısını patlayabilir. Bu nedenle, ORM'lerinizi istekli yükleme özelliklerini anlamanız ve herhangi bir sayfa isteği için sunucuya gönderdiğiniz sorgu sayısı konusunda daima uyanık olmanız gerekir.
Caching
Tüm büyük ORM'ler birinci seviye önbellek, AKA "kimlik önbellek" i korurlar; bu, kimliğine iki kez aynı varlık istemeniz durumunda, ikinci bir tur atması gerektirmeyeceği anlamına gelir ve ayrıca (veritabanınızı doğru tasarladıysanız) ) size iyimser eşzamanlılık kullanma yeteneği verir.
L1 önbelleği, L2S ve EF'de oldukça opaktır, çalışmakta olduğuna güvenmek zorundasınız. NHibernate bu konuda daha açıktır ( Get
/ Load
vs. Query
/ QueryOver
). Yine de, kimliğe göre sorgulamaya çalıştığınız sürece burada iyi olmalısınız. Birçok insan L1 önbelleğini unutur ve tekrar tekrar aynı kimliği, kimliği dışında başka bir şey tarafından tekrar tekrar arar (yani bir arama alanı). Bunu yapmanız gerekirse, daha sonra yapılacak aramalar için kimliği veya hatta tüm varlığı saklamanız gerekir.
Ayrıca seviye 2 önbellek ("sorgu önbelleği") de vardır. NHibernate bu yerleşik yapıya sahiptir. Linq to SQL ve Entity Framework , sorgu ifadesinin kendisini derleyerek uygulama sunucusu yüklerini biraz azaltmaya yardımcı olabilecek sorguları derledi , ancak verileri önbelleğe almıyor. Microsoft bu veri erişim endişesi yerine bir uygulama endişesi olarak görünüyor ve bu hem L2S hem de EF için önemli bir zayıf nokta. Söylemeye gerek yok, aynı zamanda "ham" SQL'in zayıf bir noktası. Temelde NHibernate dışındaki herhangi bir ORM ile gerçekten iyi bir performans elde etmek için kendi önbellek cephenizi uygulamanız gerekir.
Orada EF4'ü için bir L2 önbellek "uzantısı" da var tamam bir uygulama düzeyi önbelleği için toptan yedek gerçekten değil.
Sorgu Sayısı
İlişkisel veritabanları veri kümelerine dayanır . Kısa sürede büyük miktarlarda veri üretmek konusunda gerçekten başarılılar , ancak sorgu gecikmesi açısından hiçbir yere yakın değiller çünkü her komutta belirli bir miktar ek yük vardır. İyi tasarlanmış bir uygulama bu DBMS'nin güçlü yönlerine dayanmalı ve sorgu sayısını en aza indirmeli ve her birindeki veri miktarını en üst düzeye çıkarmalıdır.
Şimdi sadece bir satıra ihtiyacınız olduğunda veritabanının tamamını sorgulamak istemiyorum. Eğer gerekirse Söylediğim şey olduğunu Customer
, Address
, Phone
, CreditCard
, ve Order
sonra gereken, tek bir sayfa hizmet etmek için aynı anda satırları tüm sormak , aynı anda hepsi için ayrı ayrı sorguyu yürütmek yoktur. Bazen bundan daha kötüsü, aynı Customer
kaydı arka arkaya 5 kez sorgulayan bir kod görürsünüz , önce Id
, sonra Name
, sonra EmailAddress
, sonra ... gülünç verimsiz.
Tamamı tamamen farklı veri kümelerinde çalışan birkaç sorgu yürütmeniz gerekse bile, hepsini tek bir "komut dosyası" olarak veri tabanına göndermek ve birden fazla sonuç kümesi döndürmesini sağlamak genellikle daha etkilidir. En fazla veri topladığınız şey değil, endişelendiğiniz yüküdür.
Bu sağduyulu gibi gelebilir, ancak uygulamanın çeşitli bölümlerinde yürütülen tüm sorguların kaydını kaybetmek çoğu zaman kolaydır; Üyelik Sağlayıcınız kullanıcı / rol tablolarını, Başlık işleminiz alışveriş sepetini, Menü işleminiz site haritası tablosunu, Kenar Çubuğu işleminizin özellikli ürün listesini sorgular ve belki de sayfanız birkaç ayrı özerk alana bölünmüştür. Sipariş Geçmişi, En Son Görüntülenen, Kategori ve Envanter tablolarını ayrı ayrı sorgulayın ve bunu bilmeden önce, sayfayı sunmaya başlamadan önce 20 sorgu yürütüyorsunuz. Sadece performansı tamamen yok ediyor.
Bazı çerçeveler - ve burada esas olarak NHibernate'i düşünüyorum - bu konuda son derece zekiyiz ve tüm sorguları toplayan vadeli işlem adı verilen bir şeyi kullanmanıza ve hepsini bir kerede, en son dakikada yürütmeye çalışmanıza izin veriyor . AFAIK, bunu Microsoft teknolojilerinin herhangi biriyle yapmak istiyorsanız kendi başınızasınız; Uygulama mantığınıza eklemeniz gerekir.
İndeksleme, Tahminler ve Projeksiyonlar
Konuştuğum cihazların en az% 50'si konuştum ve hatta bazı DBA'lar endeksleri kapsayan kavramlarla ilgili sorun yaşıyor gibi görünüyor. " Customer.Name
Sütun endekslendi, yani isim üzerinde yaptığım her arama hızlı olmalı" diye düşünüyorlar. Name
Dizin , aradığınız belirli sütunu kapsamıyorsa , bu şekilde çalışmaz . De SQL Server, bununla bitti INCLUDE
de CREATE INDEX
açıklamada.
Her SELECT *
yerde saf bir şekilde kullanıyorsanız - ve eğer bir projeksiyon kullanarak açıkça belirtmediğiniz sürece her ORM'nin yapacağı şey az çoktur - o zaman DBMS, endekslerinizi tamamen göz ardı etmeyi seçebilir, çünkü bunlar kaplanmamış sütunlar içerir. Bir projeksiyon, örneğin, bunu yapmak yerine;
from c in db.Customers where c.Name == "John Doe" select c
Bunun yerine bunu yapın:
from c in db.Customers where c.Name == "John Doe"
select new { c.Id, c.Name }
Ve bu irade, en modern ORMs için, sadece gidip sorgulamak talimat Id
ve Name
tahminen endeksi kapsamındadır sütunları (ancak Email
, LastActivityDate
ya da başka hangi Orada sopa oldu sütunlar).
Ayrıca, uygun tahminleri kullanarak tüm indeksleme avantajlarını tamamen ortadan kaldırmak çok kolaydır. Örneğin:
from c in db.Customers where c.Name.Contains("Doe")
... önceki sorgumuzla neredeyse birebir aynı gözüküyor, ancak aslında tam bir tablo veya indeks taramasıyla sonuçlanacak çünkü çeviriyor LIKE '%Doe%'
. Benzer şekilde, şüpheli bir şekilde basit görünen başka bir sorgu:
from c in db.Customers where (maxDate == null) || (c.BirthDate >= maxDate)
Bir dizininiz olduğunu varsayarsak BirthDate
, bu yüklemin tamamen yararsız hale gelmesi için iyi bir şansı var. Buradaki varsayımsal programcımız açıkça bir tür dinamik sorgu oluşturmaya çalıştı ("sadece bu parametre belirtilmişse doğum tarihini filtrele"), ancak bunu yapmanın doğru yolu bu değil. Bunun yerine şöyle yazılır:
from c in db.Customers where c.BirthDate >= (maxDate ?? DateTime.MinValue)
... şimdi DB motoru bunu nasıl parametreleyeceğini ve bir indeks araması yapmayı biliyor. Sorgu ifadesinde küçük, görünüşte önemsiz bir değişiklik performansı önemli ölçüde etkileyebilir.
Ne yazık ki, genel olarak LINQ, böyle kötü sorgular yazmayı çok kolaylaştırır, çünkü bazen sağlayıcılar ne yapmaya çalıştığınızı tahmin edebilir ve sorguyu optimize edebilir, bazen de değildir. Bu yüzden , (zaten tecrübeli bir DBA'ya) gözle görülür şekilde açık olan sinir bozucu derecede tutarsız sonuçlarla bitirdiniz, eski düz SQL yazdınız.
Temel olarak her şey, hem oluşturulan SQL hem de yol açtığı yürütme planlarını yakından takip etmeniz gerektiğine ve beklediğiniz sonuçları alamamanız durumunda, bunu atlamaktan korkmayın. Arada bir ORM katmanı ve SQL el-kodu. Bu sadece EF için değil, herhangi bir ORM için de geçerlidir .
İşlemler ve Kilitleme
Milisaniyeye kadar güncel olan verileri görüntülemeniz mi gerekiyor? Belki - bağlıdır - ama muhtemelen değil. Ne yazık ki, Varlık Çerçeve seni vermeznolock
, sadece kullanabilirsiniz READ UNCOMMITTED
az işlem düzeyinde (değil tablo düzeyi). Aslında, ORM'lerin hiçbiri bu konuda özellikle güvenilir değildir; kirli okuma yapmak istiyorsanız, SQL seviyesine düşmeniz ve geçici sorgular veya saklı yordamlar yazmanız gerekir. Öyleyse, tekrar kaynayan şey, bunu çerçeve içinde yapmanın sizin için ne kadar kolay olduğu.
Entity Framework bu konuda uzun bir yol kat etti - EF'in 1. sürümü (.NET 3.5'te) çok kötüydü, "varlıklar" soyutlamasını kırmak inanılmaz derecede zordu, ama şimdi ExecuteStoreQuery ve Translate'e sahipsin , bu yüzden gerçekten çok fena değil. Bu adamlarla arkadaş olun, çünkü onları çok kullanıyor olacaksınız.
Aynı zamanda yazma kilitleme ve kilitlenme sorunu ve veritabanında mümkün olan en kısa sürede kilit tutma genel uygulaması da var. Bu bağlamda, çoğu ORM (Entity Framework dahil), aslında ham SQL'den daha iyi olma eğilimindedir, çünkü EF'de SaveChanges olan Çalışma modeli birimini içermektedir . Başka bir deyişle, kalbinizin içeriğine varlıkları "ekleyebilir" veya "güncelleyebilir" veya "silebilir", istediğiniz zaman, çalışma birimini işleyene kadar hiçbir değişikliğin veritabanına itilmeyeceği bilgisini güvence altına alabilirsiniz.
Bir UOW'nin uzun süren bir işlemle aynı olmadığını unutmayın . UOW, ORM'nin iyimser eşzamanlılık özelliklerini hala kullanıyor ve bellekteki tüm değişiklikleri takip ediyor . Son işleme kadar tek bir DML bildirimi yayınlanmaz. Bu işlem sürelerini mümkün olduğu kadar düşük tutar. Uygulamanızı ham SQL kullanarak oluşturursanız, bu ertelenen davranışı elde etmek oldukça zordur.
Bunun özellikle EF için ne anlama geldiğini: İş birimlerinizi mümkün olduğunca kaba yapın ve tam olarak ihtiyaç duymadıkça bunları işleme koymayın. Bunu yapın ve bireysel ADO.NET komutlarını rastgele zamanlarda kullanacağınızdan çok daha düşük bir kilit çekişmesi ile sonuçlanacak.
EF, yüksek trafikli / yüksek performanslı uygulamalar için tamamen iyi, diğer tüm çerçevelerde olduğu gibi yüksek trafikli / yüksek performanslı uygulamalar için gayet iyi. Önemli olan onu nasıl kullandığın. İşte en popüler çerçevelerin ve performans açısından sundukları özelliklerin hızlıca karşılaştırılması (açıklama: N = Desteklenmiyor, P = Kısmi, Y = evet / desteklenir):
Gördüğünüz gibi, EF4 (şu anki sürüm) çok kötü bir ücret almıyor, ancak performans öncelikli kaygınız ise muhtemelen en iyisi değil. NHibernate bu alanda çok daha olgun ve hatta Linq to SQL bile EF'in hala yapmadığı bazı performans arttırıcı özellikler sunuyor. Ham ADO.NET, çok özel veri erişim senaryoları için genellikle daha hızlı olacaktır , ancak tüm parçaları bir araya getirdiğinizde, çeşitli çerçevelerden elde ettiğiniz pek çok önemli fayda sunmaz.