Apakah Entity Framework 4 solusi yang baik untuk situs web publik dengan potensi 1000 hit / detik?
Dalam pemahaman saya, EF adalah solusi yang layak untuk sebagian besar situs web yang lebih kecil atau intranet, tetapi tidak akan skala dengan mudah untuk sesuatu seperti situs web komunitas populer (saya tahu SO menggunakan LINQ to SQL, tapi .. Saya ingin lebih banyak contoh / bukti. ..)
Sekarang saya berdiri di persimpangan jalan baik memilih pendekatan ADO.NET murni atau EF4. Apakah Anda pikir peningkatan produktivitas pengembang dengan EF sepadan dengan kinerja yang hilang dan akses granular ADO.NET (dengan prosedur tersimpan)? Adakah masalah serius yang mungkin dihadapi situs web dengan traffic tinggi, apakah menggunakan EF?
Terima kasih sebelumnya.
sumber
Jawaban:
Tergantung sedikit pada seberapa banyak abstraksi yang Anda butuhkan . Semuanya kompromi; misalnya, EF dan NHibernate memperkenalkan fleksibilitas yang besar untuk mewakili data dalam model yang menarik dan eksotis - tetapi sebagai akibatnya mereka lakukan menambah overhead. Overhead yang terlihat.
Jika Anda tidak perlu dapat beralih di antara penyedia basis data, dan tata letak tabel per-klien yang berbeda, dan jika data Anda utamanya dibaca , dan jika Anda tidak perlu dapat menggunakan model yang sama di EF, SSRS , Layanan Data ADO.NET, dll - maka jika Anda ingin kinerja absolut sebagai ukuran utama Anda, Anda bisa melakukan jauh lebih buruk daripada melihat necis . Dalam pengujian kami berdasarkan pada LINQ-to-SQL dan EF, kami menemukan bahwa EF secara signifikan lebih lambat dalam hal kinerja membaca mentah, mungkin karena lapisan abstraksi (antara model penyimpanan dll) dan materialisasi.
Di sini, di SO, kami sangat obsesif-kompulsif tentang kinerja mentah, dan kami senang untuk mengambil pukulan pengembangan kehilangan beberapa abstraksi untuk mendapatkan kecepatan. Dengan demikian, alat utama kami untuk menanyakan database adalah necis . Ini bahkan memungkinkan kita untuk menggunakan model LINQ-to-SQL yang sudah ada sebelumnya, tetapi cukup: ini lebih cepat. Dalam tes kinerja, ini pada dasarnya adalah kinerja yang persis sama dengan menulis semua kode ADO.NET (parameter, data-pembaca dll) secara manual, tetapi tanpa risiko salah nama kolom. Namun, ini berbasis SQL (meskipun senang menggunakan SPROC jika itu adalah racun pilihan Anda). Keuntungan dari ini adalah tidak ada pemrosesan tambahan yang terlibat, tetapi ini adalah sistem untuk orang yang menyukai SQL. Yang saya anggap: bukan hal yang buruk!
Contoh kueri, misalnya, mungkin:
yang nyaman, injeksi-aman, dll - tetapi tanpa banyak data-reader goo. Perhatikan bahwa meskipun dapat menangani partisi horizontal dan vertikal untuk memuat struktur yang kompleks, ia tidak akan mendukung pemuatan malas (tapi: kami penggemar berat pemuatan yang sangat eksplisit - lebih sedikit kejutan).
Perhatikan dalam jawaban ini saya tidak mengatakan bahwa EF tidak cocok untuk pekerjaan bervolume tinggi; hanya: Saya tahu bahwa necis itu terserah.
sumber
Pertanyaan "ORM mana yang harus saya gunakan" benar-benar menargetkan ujung gunung es besar ketika datang ke strategi akses data keseluruhan dan optimasi kinerja dalam aplikasi skala besar.
Semua hal berikut ( kira-kira dalam urutan kepentingan) akan mempengaruhi throughput, dan semuanya ditangani (kadang-kadang dengan cara yang berbeda) oleh sebagian besar kerangka kerja ORM utama di luar sana:
Desain dan Pemeliharaan Basis Data
Ini, dengan selisih yang lebar, satu-satunya penentu terpenting dari throughput aplikasi berbasis data atau situs web, dan sering kali sama sekali diabaikan oleh para programmer.
Jika Anda tidak menggunakan teknik normalisasi yang tepat, situs Anda akan hancur. Jika Anda tidak memiliki kunci utama, hampir setiap permintaan akan berjalan lambat. Jika Anda menggunakan anti-pola terkenal seperti menggunakan tabel untuk Pasangan Nilai Kunci (AKA Entity-Attribute-Value) tanpa alasan yang baik, Anda akan meledak jumlah bacaan dan tulisan fisik.
Jika Anda tidak memanfaatkan fitur yang diberikan oleh database, seperti kompresi halaman,
FILESTREAM
penyimpanan (untuk data biner),SPARSE
kolom,hierarchyid
untuk hierarki, dan sebagainya (semua contoh SQL Server), maka Anda tidak akan melihat di dekat kinerja yang bisa Anda lihat.Anda harus mulai mengkhawatirkan strategi akses data Anda setelah merancang basis data dan meyakinkan diri sendiri bahwa itu sebaik mungkin, setidaknya untuk saat ini.
Pemuatan bersemangat vs malas
Sebagian besar ORM menggunakan teknik yang disebut lazy loading untuk hubungan, yang berarti bahwa secara default akan memuat satu entitas (baris tabel) sekaligus, dan melakukan perjalanan bolak-balik ke database setiap kali perlu memuat satu atau banyak yang terkait (asing). kunci) baris.
Ini bukan hal yang baik atau buruk, itu lebih tergantung pada apa yang sebenarnya akan dilakukan dengan data, dan seberapa banyak Anda tahu di muka. Terkadang lazy-loading adalah hal yang benar untuk dilakukan. NHibernate, misalnya, dapat memutuskan untuk tidak meminta apa pun dan hanya menghasilkan proxy untuk ID tertentu. Jika yang Anda butuhkan hanyalah ID itu sendiri, mengapa harus meminta lebih banyak? Di sisi lain, jika Anda mencoba untuk mencetak pohon dari setiap elemen tunggal dalam hirarki 3-level, pemuatan malas menjadi operasi O (N²), yang sangat buruk untuk kinerja.
Satu manfaat menarik untuk menggunakan "SQL murni" (yaitu, permintaan / prosedur tersimpan ADO.NET mentah) adalah pada dasarnya memaksa Anda untuk berpikir secara tepat data apa yang diperlukan untuk menampilkan layar atau halaman tertentu. ORMs dan fitur malas-loading tidak mencegah Anda dari melakukan hal ini, tetapi mereka tidak memberi Anda kesempatan untuk menjadi ... baik, malas , dan tidak sengaja meledak jumlah pertanyaan yang Anda jalankan. Jadi, Anda perlu memahami fitur pemuatan cepat ORM Anda dan selalu waspada tentang jumlah kueri yang Anda kirim ke server untuk setiap permintaan halaman yang diberikan.
Caching
Semua ORM utama mempertahankan cache tingkat pertama, "cache identitas" AKA, yang berarti bahwa jika Anda meminta entitas yang sama dua kali dengan ID-nya, itu tidak memerlukan perjalanan pulang-pergi kedua, dan juga (jika Anda mendesain database Anda dengan benar ) memberi Anda kemampuan untuk menggunakan konkurensi optimis.
Cache L1 cukup buram di L2S dan EF, Anda harus percaya bahwa itu berfungsi. NHibernate lebih eksplisit tentang hal itu (
Get
/Load
vs.Query
/QueryOver
). Namun, selama Anda mencoba untuk query dengan ID sebanyak mungkin, Anda harus baik-baik saja di sini. Banyak orang lupa tentang cache L1 dan berulang kali mencari entitas yang sama berulang-ulang dengan sesuatu selain ID-nya (yaitu bidang pencarian). Jika Anda perlu melakukan ini maka Anda harus menyimpan ID atau bahkan seluruh entitas untuk pencarian di masa mendatang.Ada juga cache level 2 ("cache permintaan"). NHibernate memiliki fitur bawaan ini. Linq to SQL dan Entity Framework telah mengkompilasi kueri , yang dapat membantu mengurangi banyak server aplikasi dengan mengkompilasi ekspresi kueri itu sendiri, tetapi itu tidak menyimpan data. Microsoft tampaknya menganggap ini masalah aplikasi daripada masalah akses data, dan ini adalah titik kelemahan utama dari L2S dan EF. Tak perlu dikatakan itu juga merupakan titik lemah dari SQL "mentah". Untuk mendapatkan kinerja yang benar-benar bagus dengan ORM apa pun selain NHibernate, Anda perlu mengimplementasikan façade caching Anda sendiri.
Ada juga "ekstensi" cache L2 untuk EF4 yang baik - baik saja , tetapi tidak benar-benar pengganti grosir untuk cache tingkat aplikasi.
Jumlah Pertanyaan
Database relasional didasarkan pada set data. Mereka benar-benar hebat dalam menghasilkan data dalam jumlah besar dalam waktu singkat, tetapi mereka sama sekali tidak sebagus dalam hal latensi kueri karena ada sejumlah overhead tertentu yang terlibat dalam setiap perintah. Aplikasi yang dirancang dengan baik harus memainkan kekuatan dari DBMS ini dan mencoba untuk meminimalkan jumlah pertanyaan dan memaksimalkan jumlah data di masing-masing.
Sekarang saya tidak mengatakan untuk menanyakan seluruh database ketika Anda hanya perlu satu baris. Apa yang saya katakan adalah, jika Anda memerlukan
Customer
,Address
,Phone
,CreditCard
, danOrder
baris semua pada waktu yang sama untuk melayani satu halaman, maka Anda harus meminta untuk mereka semua pada waktu yang sama, tidak mengeksekusi setiap query secara terpisah. Terkadang lebih buruk dari itu, Anda akan melihat kode yang memintaCustomer
catatan yang sama 5 kali berturut-turut, pertama untuk mendapatkanId
, laluName
, laluEmailAddress
, kemudian ... itu sangat tidak efisien.Bahkan jika Anda perlu menjalankan beberapa query yang semuanya beroperasi pada set data yang sama sekali berbeda, biasanya masih lebih efisien untuk mengirim semuanya ke database sebagai "skrip" tunggal dan mengembalikan beberapa set hasil. Ini adalah overhead yang Anda khawatirkan, bukan jumlah total data.
Ini mungkin terdengar seperti akal sehat tetapi seringkali sangat mudah untuk kehilangan jejak semua pertanyaan yang sedang dieksekusi di berbagai bagian aplikasi; Penyedia Keanggotaan Anda kueri tabel pengguna / peran, tindakan Header Anda kueri keranjang belanja, tindakan Menu Anda kueri tabel peta situs, tindakan Sidebar Anda menanyakan daftar produk unggulan, dan kemudian mungkin halaman Anda dibagi menjadi beberapa area otonom terpisah yang kueri tabel Riwayat Pesanan, Baru Dilihat, Kategori, dan Inventaris secara terpisah, dan sebelum Anda mengetahuinya, Anda mengeksekusi 20 kueri bahkan sebelum Anda dapat mulai melayani halaman. Itu benar-benar menghancurkan kinerja.
Beberapa kerangka kerja - dan saya berpikir terutama dari NHibernate di sini - sangat pandai tentang hal ini dan memungkinkan Anda untuk menggunakan sesuatu yang disebut futures yang mengumpulkan seluruh pertanyaan dan mencoba mengeksekusi semuanya sekaligus, pada menit terakhir yang memungkinkan. AFAIK, Anda sendirian jika ingin melakukan ini dengan salah satu teknologi Microsoft; Anda harus membuatnya menjadi logika aplikasi Anda.
Pengindeksan, Predikat, dan Proyeksi
Setidaknya 50% dari devs yang saya ajak bicara dan bahkan beberapa DBA tampaknya memiliki masalah dengan konsep mencakup indeks. Mereka berpikir, "well,
Customer.Name
kolomnya diindeks, jadi setiap pencarian yang saya lakukan atas nama harus cepat." Kecuali itu tidak berfungsi seperti itu kecualiName
indeks mencakup kolom spesifik yang Anda cari. Dalam SQL Server, yang dilakukan denganINCLUDE
dalamCREATE INDEX
pernyataan.Jika Anda menggunakan secara naif di
SELECT *
mana-mana - dan itu lebih atau kurang dari apa yang akan dilakukan setiap ORM kecuali Anda secara eksplisit menentukan lain menggunakan proyeksi - maka DBMS mungkin sangat memilih untuk sepenuhnya mengabaikan indeks Anda karena mengandung kolom yang tidak tercakup. Proyeksi berarti bahwa, misalnya, daripada melakukan ini:Anda melakukan ini sebagai gantinya:
Dan kehendak ini, untuk sebagian besar ORMs modern, menginstruksikan untuk hanya pergi dan query
Id
danName
kolom yang mungkin ditutupi oleh indeks (tapi bukanEmail
,LastActivityDate
, atau apa pun kolom yang kebetulan menempel di sana lain).Ini juga sangat mudah untuk sepenuhnya menghilangkan manfaat pengindeksan dengan menggunakan predikat yang tidak pantas. Sebagai contoh:
... terlihat hampir identik dengan permintaan kami sebelumnya tetapi pada kenyataannya akan menghasilkan tabel penuh atau pemindaian indeks karena diterjemahkan menjadi
LIKE '%Doe%'
. Demikian pula, permintaan lain yang terlihat sederhana dan mencurigakan adalah:Dengan asumsi Anda memiliki indeks
BirthDate
, predikat ini memiliki peluang bagus untuk menjadikannya benar-benar tidak berguna. Programmer hipotetis kami di sini jelas telah berusaha membuat semacam kueri dinamis ("hanya filter tanggal lahir jika parameter itu ditentukan"), tetapi ini bukan cara yang tepat untuk melakukannya. Ditulis seperti ini sebagai gantinya:... sekarang mesin DB tahu bagaimana membuat parameter ini dan melakukan pencarian indeks. Satu perubahan kecil, yang tampaknya tidak signifikan terhadap ekspresi kueri dapat secara drastis memengaruhi kinerja.
Sayangnya LINQ secara umum membuat semuanya terlalu mudah untuk menulis kueri buruk seperti ini karena kadang - kadang penyedia dapat menebak apa yang Anda coba lakukan dan mengoptimalkan kueri, dan kadang-kadang tidak. Jadi, Anda berakhir dengan hasil yang sangat tidak konsisten yang pasti sangat menyolok (untuk DBA yang berpengalaman), seandainya Anda baru saja menulis SQL lama.
Pada dasarnya itu semua bermuara pada fakta bahwa Anda benar-benar harus mengawasi baik-baik SQL yang dihasilkan dan rencana eksekusi yang mereka tuju, dan jika Anda tidak mendapatkan hasil yang Anda harapkan, jangan takut untuk mem-bypass Lapisan ORM sesekali dan tangan-kode SQL. Ini berlaku untuk ORM apa pun , tidak hanya EF.
Transaksi dan Penguncian
Apakah Anda perlu menampilkan data yang terkini hingga milidetik? Mungkin - itu tergantung - tetapi mungkin tidak. Sayangnya, Entity Framework tidak memberi Anda
nolock
, Anda hanya dapat menggunakanREAD UNCOMMITTED
di level transaksi (bukan level tabel). Faktanya tidak ada ORM yang bisa diandalkan tentang hal ini; jika Anda ingin melakukan pembacaan yang kotor, Anda harus turun ke tingkat SQL dan menulis pertanyaan ad-hoc atau prosedur yang tersimpan. Jadi intinya, sekali lagi, adalah betapa mudahnya bagi Anda untuk melakukan itu dalam kerangka kerja.Entity Framework telah datang jauh dalam hal ini - versi 1 dari EF (dalam .NET 3.5) sangat mengerikan, membuatnya sangat sulit untuk menembus abstraksi "entitas", tetapi sekarang Anda memiliki ExecuteStoreQuery dan Translate , jadi itu benar-benar lumayan. Bertemanlah dengan orang-orang ini karena Anda akan sering menggunakannya.
Ada juga masalah menulis penguncian dan kebuntuan dan praktik umum memegang kunci dalam database sesedikit mungkin. Dalam hal ini, sebagian besar ORM (termasuk Entity Framework) sebenarnya cenderung lebih baik daripada SQL mentah karena mereka merangkum unit pola Kerja , yang dalam EF adalah SaveChanges . Dengan kata lain, Anda dapat "menyisipkan" atau "memperbarui" atau "menghapus" entitas ke isi hati Anda, kapan pun Anda mau, aman dengan pengetahuan bahwa tidak ada perubahan yang akan didorong ke database hingga Anda melakukan unit kerja.
Perhatikan bahwa UOW tidak analog dengan transaksi yang sudah berjalan lama. UOW masih menggunakan fitur konkurensi optimis dari ORM dan melacak semua perubahan dalam memori . Tidak satu pun pernyataan DML yang dikeluarkan sampai komit terakhir. Ini menjaga waktu transaksi serendah mungkin. Jika Anda membangun aplikasi menggunakan SQL mentah, cukup sulit untuk mencapai perilaku yang ditangguhkan ini.
Apa artinya ini untuk EF secara spesifik: Jadikan unit kerja Anda seringkas mungkin dan jangan komit sampai Anda benar-benar perlu. Lakukan ini dan Anda akan berakhir dengan pertentangan kunci yang jauh lebih rendah daripada yang Anda akan gunakan perintah ADO.NET individu pada waktu yang acak.
Kesimpulannya:
EF benar-benar baik untuk aplikasi lalu lintas tinggi / kinerja tinggi, sama seperti setiap kerangka kerja lainnya baik untuk aplikasi lalu lintas tinggi / kinerja tinggi. Yang penting adalah bagaimana Anda menggunakannya. Berikut ini adalah perbandingan cepat kerangka kerja paling populer dan fitur apa yang mereka tawarkan dalam hal kinerja (legenda: N = Tidak didukung, P = Sebagian, Y = ya / didukung):
Seperti yang Anda lihat, EF4 (versi saat ini) tidak terlalu mahal, tetapi mungkin bukan yang terbaik jika kinerja menjadi perhatian utama Anda. NHibernate jauh lebih matang di bidang ini dan bahkan Linq to SQL menyediakan beberapa fitur peningkatan kinerja yang EF masih belum. Raw ADO.NET sering akan lebih cepat untuk skenario akses data yang sangat spesifik , tetapi, ketika Anda menggabungkan semua bagian, itu benar-benar tidak menawarkan banyak manfaat penting yang Anda dapatkan dari berbagai kerangka kerja.
Dan, hanya untuk memastikan bahwa saya terdengar seperti rekaman yang rusak, semua ini tidak ada masalah sedikitpun jika Anda tidak merancang database, aplikasi, dan strategi akses data Anda dengan benar. Semua item dalam bagan di atas adalah untuk meningkatkan kinerja di luar garis dasar; sebagian besar waktu, baseline itu sendiri adalah yang paling membutuhkan perbaikan.
sumber
Sunting: Berdasarkan pada @Aaronaught jawaban yang bagus Saya menambahkan beberapa poin yang menargetkan kinerja dengan EF. Poin-poin baru diawali oleh Edit.
Peningkatan terbesar dalam kinerja di situs web dengan lalu lintas tinggi dicapai dengan caching (= pertama-tama menghindari pemrosesan server web atau permintaan basis data) diikuti dengan pemrosesan asinkron untuk menghindari pemblokiran thread saat query basis data dilakukan.
Tidak ada jawaban bukti peluru untuk pertanyaan Anda karena selalu tergantung pada persyaratan untuk aplikasi dan pada kompleksitas pertanyaan. Yang benar adalah bahwa produktivitas pengembang dengan EF menyembunyikan kompleksitas di baliknya yang dalam banyak kasus menyebabkan penggunaan EF yang salah dan kinerja yang buruk. Gagasan bahwa Anda dapat mengekspos antarmuka abstrak tingkat tinggi untuk akses data dan itu akan berfungsi dengan baik dalam semua kasus tidak bekerja. Bahkan dengan ORM Anda harus tahu apa yang terjadi di balik abstraksi dan bagaimana menggunakannya dengan benar.
Jika Anda tidak memiliki pengalaman sebelumnya dengan EF, Anda akan menghadapi banyak tantangan saat berhadapan dengan kinerja. Anda dapat membuat lebih banyak kesalahan saat bekerja dengan EF dibandingkan dengan ADO.NET. Juga ada banyak pemrosesan tambahan yang dilakukan di EF, sehingga EF akan selalu jauh lebih lambat daripada ADO.NET asli - itu adalah sesuatu yang dapat Anda ukur dengan bukti sederhana aplikasi konsep.
Jika Anda ingin mendapatkan kinerja terbaik dari EF, Anda kemungkinan besar harus:
MergeOption.NoTracking
SqlCommand
berisi banyak insert, pembaruan atau penghapusan tetapi dengan EF setiap perintah tersebut akan dieksekusi secara terpisah ke database.GetByKey
di ObjectContext API atauFind
di DbContext API) untuk menanyakan cache terlebih dahulu. Jika Anda menggunakan Linq-to-entitas atau ESQL, ia akan membuat bolak-balik ke database dan setelah itu akan mengembalikan instance yang ada dari cache.Saya tidak yakin apakah SO masih menggunakan L2S. Mereka mengembangkan ORM open source baru yang disebut Dapper dan saya pikir poin utama di balik pengembangan ini adalah peningkatan kinerja.
sumber