Kerangka Entitas Terlalu Lambat. Apa saja pilihan saya? [Tutup]

93

Saya telah mengikuti mantra "Don't Optimize Prematurely" dan mengkodekan Layanan WCF saya menggunakan Entity Framework.

Namun, saya memprofilkan kinerja dan Entity Framework terlalu lambat. (Aplikasi saya memproses 2 pesan dalam waktu sekitar 1,2 detik, di mana aplikasi (lama) yang saya tulis ulang melakukan 5-6 pesan dalam waktu yang bersamaan. (Aplikasi lama memanggil sprocs untuk DB Access-nya.)

Profiling saya menunjuk ke Entity Framework yang menghabiskan sebagian besar waktu per pesan.

Jadi, apa saja pilihan saya?

  • Apakah ada ORM yang lebih baik di luar sana?
    (Sesuatu yang hanya mendukung membaca dan menulis objek secara normal dan melakukannya dengan cepat ..)

  • Apakah ada cara untuk membuat Entity Framework lebih cepat?
    ( Catatan : ketika saya mengatakan lebih cepat, maksud saya dalam jangka panjang, bukan panggilan pertama. (Panggilan pertama lambat (15 detik untuk sebuah pesan), tapi itu bukan masalah. Saya hanya perlu cepat untuk sisanya dari pesan.)

  • Beberapa opsi misterius ke-3 yang akan membantu saya mendapatkan lebih banyak kecepatan dari layanan saya.

CATATAN: Sebagian besar interaksi DB saya adalah Buat dan Perbarui. Saya sangat sedikit memilih dan menghapus.

Vaccano
sumber
Ini terdengar seperti pengulangan 'linq lambat' bagaimana Anda tahu itu EF? Sudahkah Anda membuat profil semua perubahan Anda?
Maess
6
Beberapa jawaban mengarah ke pertanyaan. Menurut pengalaman saya, kelambatan di EF tidak ada hubungannya dengan kueri melainkan dengan biaya materialisasi, dan biaya tersebut sering dikaitkan dengan pelacakan perubahan dan bagaimana hal itu memengaruhi instans yang dibuat. Sayangnya, saya tidak memiliki solusi yang tepat untuk Anda, jadi ini hanya sebuah komentar, tetapi saya akan merekomendasikan untuk melihat apakah pembuatan profil mengungkapkan biaya materialisasi yang tinggi dan, jika demikian, teliti apa yang dapat dilakukan tentang biaya tersebut.
Anthony Pegram
@Maess - Saya pikir saya mengindikasikan bahwa saya telah membuat profil dan menemukan bahwa EF / DB yang lambat. Either way, ya saya lakukan. Saya memprofilkannya dan interaksi EF / DBlah yang menjadi penyebab utamanya.
Vaccano
@Anthony - Bukankah perwujudan pertama kali menjalankan hal-hal semacam itu? Jika demikian, Anda benar bahwa itu sangat lambat. Proses pertama sangat lambat. Tapi seperti yang saya tunjukkan, saya tidak terlalu khawatir tentang itu. Ini adalah total throughput yang menjadi masalah. (Jika bukan itu yang Terwujud maka saya perlu melakukan beberapa penelitian untuk melihat apakah itu penyebab masalah saya)
Vaccano
1
@Vaccano, tidak, materialisasi adalah proses mengambil data dari database dan membuat instance dan mengisi grafik objek untuk merepresentasikan data itu. Saya tidak berbicara tentang kinerja proses pertama karena kodenya jitted (atau bahkan karena Sql Server mungkin membuat rencana eksekusi kueri), tetapi apa yang terjadi setiap kali Anda mendapatkan data dalam bentuk objek.
Anthony Pegram

Jawaban:

45

Anda harus mulai dengan membuat profil perintah SQL yang sebenarnya dikeluarkan oleh Entity Framework. Bergantung pada konfigurasi Anda (POCO, entitas Self-Tracking), ada banyak ruang untuk pengoptimalan. Anda dapat men-debug perintah SQL (yang seharusnya tidak berbeda antara mode debug dan rilis) menggunakanObjectSet<T>.ToTraceString() metode ini. Jika Anda menemukan kueri yang memerlukan pengoptimalan lebih lanjut, Anda dapat menggunakan beberapa proyeksi untuk memberi EF lebih banyak informasi tentang apa yang ingin Anda capai.

Contoh:

Product product = db.Products.SingleOrDefault(p => p.Id == 10);
// executes SELECT * FROM Products WHERE Id = 10

ProductDto dto = new ProductDto();
foreach (Category category in product.Categories)
// executes SELECT * FROM Categories WHERE ProductId = 10
{
    dto.Categories.Add(new CategoryDto { Name = category.Name });
}

Bisa diganti dengan:

var query = from p in db.Products
            where p.Id == 10
            select new
            {
                p.Name,
                Categories = from c in p.Categories select c.Name
            };
ProductDto dto = new ProductDto();
foreach (var categoryName in query.Single().Categories)
// Executes SELECT p.Id, c.Name FROM Products as p, Categories as c WHERE p.Id = 10 AND p.Id = c.ProductId
{
    dto.Categories.Add(new CategoryDto { Name = categoryName });
}

Saya baru saja mengetiknya dari kepala saya, jadi ini bukanlah cara yang tepat untuk mengeksekusinya, tetapi EF sebenarnya melakukan beberapa pengoptimalan yang bagus jika Anda menceritakan semua yang Anda ketahui tentang kueri (dalam hal ini, kita akan membutuhkan kategori- nama). Tetapi ini tidak seperti eager-loading (db.Products.Include ("Categories")) karena proyeksi dapat mengurangi jumlah data yang akan dimuat.

J. Tihon
sumber
40
Tanggapan ini terdengar masuk akal, sampai Anda menyadari bahwa jenis anonim tidak dapat diakses di luar metode yang mereka tentukan. Jika Anda ingin memuat objek yang kompleks dan tidak menulis megamoth, Anda perlu menghilangkan jenis anonim baru Anda menjadi semacam POCO. Sekali lagi, itu kedengarannya masuk akal sampai Anda menyadari bahwa dengan melakukan itu Anda pada dasarnya telah MEMULIHKAN KERANGKA ENTITAS ANDA SENDIRI. Itu omong kosong.
Doug
5
ini menghasilkan peningkatan kecepatan 15x-20x bagi saya.
Dave Cousineau
12
Balasan yang menarik dan bermanfaat, masih berlaku beberapa waktu kemudian. @Doug: Yang tidak benar-benar omong kosong karena Anda hanya mengoptimalkan (menggunakan proyeksi) beberapa kueri di mana Anda benar-benar perlu menggunakan manfaat tambahan. EF dan POCO memberi Anda default yang wajar, yang sangat bagus!
Victor
2
@Doug Sebagian besar aplikasi memiliki model tampilan untuk skenario hanya lihat, bukan? Sebaiknya lakukan pemetaan sebanyak mungkin saat Anda mengeluarkan data.
Casey
4
Saya dulu merasa ORM adalah masa depan. Mereka masuk akal, sampai saya mulai menggunakannya. Kemudian saya menemukan Dapper . Sekarang, ketika melihat solusi seperti ini, saya ngeri melihat bagaimana kompleksitas meningkat dengan cepat. Menulis SQL yang diabstraksikan dalam C # bukanlah cara untuk menjalani hidup.
Michael Silver
80

Faktanya adalah bahwa produk seperti Entity Framework SELALU akan lambat dan tidak efisien, karena mereka mengeksekusi lebih banyak kode.

Saya juga merasa konyol bahwa orang menyarankan bahwa seseorang harus mengoptimalkan kueri LINQ, melihat SQL yang dihasilkan, menggunakan debugger, melakukan pra-kompilasi, mengambil banyak langkah tambahan, dll. Yaitu membuang banyak waktu. Tidak ada yang mengatakan - Sederhanakan! Semua orang ingin membuat masalah lebih rumit dengan mengambil lebih banyak langkah (membuang-buang waktu).

Pendekatan akal sehat adalah tidak menggunakan EF atau LINQ sama sekali. Gunakan SQL biasa. Tidak ada yang salah dengan itu. Hanya karena ada mentalitas berkelompok di antara para programmer dan mereka merasakan dorongan untuk menggunakan setiap produk baru yang ada, tidak berarti itu bagus atau akan berhasil. Kebanyakan programmer berpikir jika mereka menggabungkan setiap potongan kode baru yang dirilis oleh perusahaan besar, itu membuat mereka menjadi programmer yang lebih pintar; tidak benar sama sekali. Pemrograman cerdas sebagian besar adalah tentang bagaimana melakukan lebih banyak dengan lebih sedikit sakit kepala, ketidakpastian, dan dalam waktu yang paling sedikit. Ingat waktu! Itu adalah elemen yang paling penting, jadi cobalah untuk menemukan cara untuk tidak menyia-nyiakannya untuk memecahkan masalah dalam kode buruk / membengkak yang ditulis hanya untuk menyesuaikan dengan beberapa yang disebut 'pola' aneh

Santai, nikmati hidup, istirahat dari pengkodean dan berhenti menggunakan fitur tambahan, kode, produk, 'pola'. Hidup itu singkat dan umur kode Anda bahkan lebih pendek, dan ini tentu saja bukan ilmu roket. Hapus layer seperti LINQ, EF dan lainnya, dan kode Anda akan berjalan secara efisien, akan diskalakan, dan ya, itu akan tetap mudah untuk dipelihara. Terlalu banyak abstraksi adalah 'pola' yang buruk.

Dan itulah solusi untuk masalah Anda.

Sean
sumber
156
Ini membuang bayi dengan air mandi. Anda mengoptimalkan kemacetan, konyol membuang EF karena terlalu lambat di beberapa tempat, sementara di kebanyakan tempat lain terlalu cepat. Mengapa tidak menggunakan keduanya? EF menangani prosedur tersimpan dan SQL mentah dengan baik. Saya baru saja mengubah kueri LINQ-to-SQL yang membutuhkan 10+ detik menjadi SP yang membutuhkan ~ 1 detik, tetapi saya tidak akan membuang semua LINQ-to-SQL. Ini menghemat BANYAK waktu dalam kasus lain yang lebih sederhana, dengan lebih sedikit kode dan lebih sedikit ruang untuk kesalahan dan kueri diverifikasi kompiler dan cocok dengan database. Lebih sedikit kode lebih mudah perawatan dan lebih sedikit ruang untuk kesalahan.
JulianR
12
Secara keseluruhan, saran Anda bagus, tetapi menurut saya tidak benar untuk meninggalkan EF atau abstraksi lain karena 10% dari mereka tidak bekerja dengan baik.
JulianR
50
SQL biasa = mudah dirawat? Tidak benar untuk aplikasi yang sangat besar dengan banyak logika bisnis. Menulis SQL kompleks yang dapat digunakan kembali bukanlah hal yang mudah untuk dilakukan. Secara pribadi saya memiliki beberapa masalah kinerja dengan EF, tapi masalah ini tidak sebanding dengan manfaat dari ORM yang tepat dalam hal RAD dan menjaga agar tetap KERING (jika ada tingkat kerumitan yang terlibat).
MemeDeveloper
13
+ 10 ^ 100 Terlalu banyak abstraksi adalah 'pola' yang buruk
Makach
58
-1. "EF SELALU akan lambat dan tidak efisien." Saya tidak mengerti mengapa Anda menyatakan sesuatu seperti ini sebagai kebenaran mutlak. Memiliki lebih banyak lapisan untuk dilalui akan membuat sesuatu lebih lambat, tetapi apakah perbedaan itu bahkan DAPAT DIPERHATIKAN sepenuhnya tergantung pada situasi seperti jumlah data dan jenis kueri yang dieksekusi. Bagi saya ini sama saja dengan mengatakan 'C # akan SELALU lambat dan tidak efisien' karena ini adalah abstraksi yang lebih tinggi daripada C ++. Namun banyak orang memilih untuk menggunakannya karena produktivitas memperoleh keuntungan yang jauh lebih besar daripada kerugian kinerja (jika ada). Hal yang sama berlaku untuk EF
Despertar
37

Satu saran adalah menggunakan LINQ ke Entity Framework hanya untuk pernyataan CRUD rekaman tunggal.

Untuk kueri, pencarian, pelaporan, dll. Yang lebih terlibat, tulis prosedur tersimpan dan tambahkan ke model Entity Framework seperti yang dijelaskan di MSDN .

Ini adalah pendekatan yang saya ambil dengan beberapa situs saya dan tampaknya ini merupakan kompromi yang baik antara produktivitas dan kinerja. Entity Framework tidak selalu menghasilkan SQL paling efisien untuk tugas yang ada. Dan daripada menghabiskan waktu untuk mencari tahu mengapa, menulis prosedur tersimpan untuk query yang lebih kompleks sebenarnya menghemat waktu bagi saya. Setelah Anda terbiasa dengan prosesnya, tidak terlalu merepotkan untuk menambahkan procs tersimpan ke model EF Anda. Dan tentu saja manfaat menambahkannya ke model Anda adalah Anda mendapatkan semua kebaikan yang diketik dengan kuat yang berasal dari penggunaan ORM.

Steve Wortham
sumber
Apakah Anda memiliki gambaran tentang metode yang digunakan dalam scaffolding seperti db.athlete.find (id) dll. Bagaimana kinerjanya dibandingkan dengan ADO.NET atau necis ??
Ini jebakan
15

Jika Anda murni mengambil data, akan sangat membantu kinerja saat Anda memberi tahu EF untuk tidak melacak entitas yang diambilnya. Lakukan ini dengan menggunakan MergeOption.NoTracking. EF hanya akan menghasilkan kueri, mengeksekusinya, dan deserialisasi hasilnya ke objek, tetapi tidak akan mencoba melacak perubahan entitas atau apa pun yang bersifat seperti itu. Jika kueri sederhana (tidak menghabiskan banyak waktu menunggu database kembali), saya telah menemukan bahwa pengaturan ke NoTracking dapat menggandakan kinerja kueri.

Lihat artikel MSDN ini di MergeOption enum:

Resolusi Identitas, Manajemen Status, dan Pelacakan Perubahan

Ini sepertinya artikel yang bagus tentang kinerja EF:

Kinerja dan Kerangka Entitas

JulianR
sumber
9
Sebelum ada yang melakukan ini, mungkin ada baiknya untuk membaca di sini. stackoverflow.com/questions/9259480/…
leen3o
6

Anda mengatakan bahwa Anda telah membuat profil aplikasi. Sudahkah Anda membuat profil ORM juga? Ada profiler EF dari Ayende yang akan menyoroti di mana Anda dapat mengoptimalkan kode EF Anda. Anda dapat menemukannya di sini:

http://efprof.com/

Ingatlah bahwa Anda dapat menggunakan pendekatan SQL tradisional bersama ORM Anda jika Anda perlu untuk mendapatkan kinerja.

Jika ada ORM yang lebih cepat / lebih baik? Bergantung pada objek / model data Anda, Anda dapat mempertimbangkan untuk menggunakan salah satu ORM mikro, seperti Dapper , Massive , atau PetaPoco .

Situs Dapper menerbitkan beberapa tolok ukur komparatif yang akan memberi Anda gambaran bagaimana perbandingannya dengan ORM lainnya. Namun perlu dicatat bahwa ORM mikro tidak mendukung kumpulan fitur kaya dari ORM lengkap seperti EF dan NH.

Anda mungkin ingin melihat RavenDB . Ini adalah database non-relasional (dari Ayende lagi) yang memungkinkan Anda menyimpan POCO secara langsung tanpa memerlukan pemetaan . RavenDB dioptimalkan untuk pembacaan dan membuat hidup developer jauh lebih mudah dengan menghilangkan kebutuhan untuk memanipulasi skema dan memetakan objek Anda ke skema itu. Namun, ketahuilah bahwa ini adalah pendekatan yang sangat berbeda untuk menggunakan pendekatan ORM dan ini diuraikan di situs produk .

Sean Kearon
sumber
4

Saya telah menemukan jawaban dari @Slauma di sini sangat berguna untuk mempercepat segalanya. Saya menggunakan jenis pola yang sama untuk penyisipan dan pembaruan - dan kinerja meroket.

MemeDeveloper
sumber
2

Dari pengalaman saya, masalahnya bukan dengan EF, tapi dengan pendekatan ORM itu sendiri.

Secara umum semua ORM menderita masalah N + 1 tidak dioptimalkan kueri dan lain-lain. Tebakan terbaik saya adalah melacak kueri yang menyebabkan penurunan kinerja dan mencoba menyetel alat ORM, atau menulis ulang bagian itu dengan SPROC.

Valera Kolupaev
sumber
2
Orang-orang terus memberitahuku ini. Tetapi saya akan menyiapkan pernyataan pemilihan sederhana menggunakan ADO sekolah lama, dan pemilihan sederhana yang sama menggunakan konteks EF dan EF selalu jauh lebih lambat. Saya benar-benar ingin menyukai EF, tetapi itu membuat hidup lebih sulit daripada lebih mudah.
Sinaesthetic
1
@Sinaesthetic Tentu saja lebih lambat. Dengan cara yang sama, kode yang ditulis menggunakan Linq ke Objek biasanya lebih lambat daripada kode yang ditulis tanpanya. Pertanyaannya adalah tidak benar-benar apakah itu lebih cepat atau lebih cepat (bagaimana bisa, ketika di bawah tenda masih harus mengeluarkan query Anda mengeluarkan dengan tangan?) Tapi apakah 1) itu masih cukup cepat untuk kebutuhan anda 2) menghemat waktu Anda menulis kode 3) manfaatnya mengimbangi biaya. Berdasarkan item tersebut menurut saya EF sesuai untuk banyak proyek.
Casey
@Sinaesthetic Saya juga akan menambahkan bahwa jika Anda tidak menggunakan ORM, yang terjadi lebih sering daripada tidak adalah bahwa setiap kueri SQL disetel dengan baik dan dioptimalkan tetapi aplikasi akhirnya mengembangkan in-house, organik, buruk ORM yang didukung dan berkinerja buruk, kecuali tim Anda sangat disiplin dan sangat memperhatikan kinerja.
Casey
1

Saya mengalami masalah ini juga. Saya benci membuang ke EF karena bekerja dengan sangat baik, tapi lambat. Dalam kebanyakan kasus, saya hanya ingin mencari catatan atau pembaruan / penyisipan. Bahkan operasi sederhana seperti ini lambat. Saya menarik kembali 1100 catatan dari tabel ke dalam Daftar dan operasi itu memakan waktu 6 detik dengan EF. Bagi saya ini terlalu lama, bahkan menabung pun butuh waktu lama.

Saya akhirnya membuat ORM saya sendiri. Saya menarik 1100 record yang sama dari database dan ORM saya membutuhkan waktu 2 detik, jauh lebih cepat daripada EF. Segala sesuatu dengan ORM saya hampir instan. Satu-satunya batasan saat ini adalah bahwa ini hanya bekerja dengan MS SQL Server, tetapi dapat diubah untuk bekerja dengan yang lain seperti Oracle. Saya menggunakan MS SQL Server untuk semuanya sekarang.

Jika Anda ingin mencoba ORM saya, berikut adalah tautan dan situs webnya:

https://github.com/jdemeuse1204/OR-M-Data-Entities

Atau jika Anda ingin menggunakan nugget:

PM> Instal-Paket ATAU-M_DataEntities

Dokumentasi juga ada di sana

TheMiddleMan
sumber
0

Masuk akal untuk mengoptimalkan setelah Anda membuat profil. Jika Anda mengetahui bahwa akses DB lambat, Anda dapat mengonversi menggunakan prosedur tersimpan dan tetap menggunakan EF. Jika Anda mengetahui bahwa EF itu sendiri yang lambat, Anda mungkin harus beralih ke ORM yang berbeda atau tidak menggunakan ORM sama sekali.

Gabe
sumber
0

Kami memiliki aplikasi serupa (Wcf -> EF -> database) yang melakukan 120 Permintaan per detik dengan mudah, jadi saya lebih dari yakin bahwa EF bukan masalah Anda di sini, karena itu, saya telah melihat peningkatan kinerja yang besar dengan kueri yang dikompilasi.

np-keras
sumber
98% kode saya adalah Buat dan Perbarui panggilan. Saya tidak tahu apakah itu membuat perbedaan, tetapi itu jauh lebih lambat dari 120 per detik.
Vaccano
ya itu bukan aplikasi yang khas, saya sarankan Anda membuat profil aplikasi Anda. bagi kami ini sebagian besar terbaca ...
np-hard
0

Saya menggunakan EF, LINQ ke SQL dan rapi. Dapper adalah yang tercepat. Contoh: Saya membutuhkan 1000 catatan utama dengan masing-masing 4 sub catatan. Saya menggunakan LINQ ke sql, butuh waktu sekitar 6 detik. Saya kemudian beralih ke necis, mengambil 2 kumpulan record dari satu prosedur tersimpan dan untuk setiap record menambahkan sub record. Total waktu 1 detik.

Juga prosedur tersimpan menggunakan fungsi nilai tabel dengan penerapan silang, saya menemukan fungsi nilai skalar menjadi sangat lambat.

Saran saya adalah menggunakan EF atau LINQ ke SQL dan untuk situasi tertentu beralih ke rapi.

tfa
sumber
-1

Kerangka Entitas seharusnya tidak menyebabkan kemacetan besar itu sendiri. Kemungkinannya ada penyebab lain. Anda dapat mencoba mengalihkan EF ke Linq2SQL, keduanya memiliki fitur pembanding dan kodenya harus mudah dikonversi tetapi dalam banyak kasus Linq2SQL lebih cepat daripada EF.

Wiktor Zychla
sumber