Bagikan melalui


Pemodelan untuk Performa

Dalam banyak kasus, cara Anda memodelkan dapat berdampak besar pada performa aplikasi Anda; sementara model yang dinormalisasi dengan benar dan "benar" biasanya merupakan titik awal yang baik, dalam aplikasi dunia nyata beberapa kompromi pragmatis dapat pergi jauh untuk mencapai performa yang baik. Karena cukup sulit untuk mengubah model Anda setelah aplikasi berjalan dalam produksi, ada baiknya mengingat performa saat membuat model awal.

Denormalisasi dan penembolokan

Denormalisasi adalah praktik menambahkan data redundan ke skema Anda, biasanya untuk menghilangkan gabungan saat mengkueri. Misalnya, untuk model dengan Blog dan Postingan, di mana setiap Postingan memiliki Peringkat, Anda mungkin diharuskan untuk sering menunjukkan peringkat rata-rata Blog. Pendekatan sederhana untuk ini akan mengelompokkan Posting menurut Blog mereka, dan menghitung rata-rata sebagai bagian dari kueri; tetapi ini membutuhkan gabungan yang mahal di antara dua tabel. Denormalisasi akan menambahkan rata-rata terhitung dari semua posting ke kolom baru di Blog, sehingga segera dapat diakses, tanpa bergabung atau menghitung.

Hal di atas dapat dilihat sebagai bentuk penembolokan - informasi agregat dari Postingan di-cache di Blog mereka; dan seperti halnya penembolokan apa pun, masalahnya adalah cara menjaga nilai cache tetap terbaru dengan data yang di-cache. Dalam banyak kasus, tidak apa-apa bagi data yang di-cache untuk jeda sedikit; misalnya, dalam contoh di atas, biasanya masuk akal agar peringkat rata-rata blog tidak sepenuhnya diperbarui pada titik tertentu. Jika demikian, Anda dapat menghitungnya kembali setiap saat dan kemudian; jika tidak, sistem yang lebih rumit harus disiapkan untuk menjaga nilai yang di-cache tetap terbarui.

Berikut ini merinci beberapa teknik untuk denormalisasi dan penembolokan di EF Core, dan menunjuk ke bagian yang relevan dalam dokumentasi.

Kolom komputasi tersimpan

Jika data yang akan di-cache adalah produk kolom lain dalam tabel yang sama, maka kolom komputasi yang disimpan dapat menjadi solusi yang sempurna. Misalnya, Customer mungkin memiliki FirstName kolom dan LastName , tetapi kita mungkin perlu mencari berdasarkan nama lengkap pelanggan. Kolom komputasi yang disimpan secara otomatis dikelola oleh database - yang menghitung ulang setiap kali baris diubah - dan Anda bahkan dapat menentukan indeks di atasnya untuk mempercepat kueri.

Memperbarui kolom cache saat input berubah

Jika kolom cache Anda perlu mereferensikan input dari luar baris tabel, Anda tidak dapat menggunakan kolom komputasi. Namun, masih mungkin untuk menghitung ulang kolom setiap kali inputnya berubah; misalnya, Anda dapat menghitung ulang peringkat Blog rata-rata setiap kali Posting diubah, ditambahkan, atau dihapus. Pastikan untuk mengidentifikasi kondisi yang tepat ketika perhitungan ulang diperlukan, jika tidak, nilai cache Anda akan tidak sinkron.

Salah satu cara untuk melakukan ini, adalah dengan melakukan pembaruan sendiri, melalui API Inti EF reguler. SaveChanges Peristiwa atau pencegat dapat digunakan untuk secara otomatis memeriksa apakah ada Postingan yang diperbarui, dan untuk melakukan penghasilan ulang dengan cara itu. Perhatikan bahwa ini biasanya memerlukan roundtrips database tambahan, karena perintah tambahan harus dikirim.

Untuk aplikasi yang lebih sensitif terhadap perf, pemicu database dapat didefinisikan untuk secara otomatis melakukan perhitungan ulang dalam database. Ini menyimpan roundtrips database tambahan, secara otomatis terjadi dalam transaksi yang sama dengan pembaruan utama, dan dapat lebih sederhana untuk disiapkan. EF tidak menyediakan API tertentu untuk membuat atau memelihara pemicu, tetapi sangat baik untuk membuat migrasi kosong dan menambahkan definisi pemicu melalui SQL mentah.

Tampilan materialisasi/terindeks

Tampilan materialisasi (atau terindeks) mirip dengan tampilan reguler, kecuali bahwa data mereka disimpan di disk ("terwujud"), daripada dihitung setiap kali tampilan dikueri. Tampilan tersebut secara konseptual mirip dengan kolom komputasi yang disimpan, karena mereka menyimpan cache hasil perhitungan yang berpotensi mahal; namun, mereka menyimpan seluruh hasil kueri alih-alih satu kolom. Tampilan materialisasi dapat dikueri sama seperti tabel biasa, dan karena di-cache pada disk, kueri tersebut dijalankan dengan sangat cepat dan murah tanpa harus terus-menerus melakukan perhitungan kueri yang mahal yang menentukan tampilan.

Dukungan khusus untuk tampilan materialisasi bervariasi di seluruh database. Dalam beberapa database (misalnya PostgreSQL), tampilan materialisasi harus disegarkan secara manual agar nilainya disinkronkan dengan tabel yang mendasarnya. Ini biasanya dilakukan melalui timer - dalam kasus di mana beberapa jeda data dapat diterima - atau melalui pemicu atau panggilan prosedur tersimpan dalam kondisi tertentu. Tampilan Terindeks SQL Server, di sisi lain, secara otomatis diperbarui saat tabel yang mendasar dimodifikasi; ini memastikan bahwa tampilan selalu menampilkan data terbaru, dengan biaya pembaruan yang lebih lambat. Selain itu, Tampilan Indeks SQL Server memiliki berbagai batasan pada apa yang mereka dukung; lihat dokumentasi untuk informasi lebih lanjut.

EF saat ini tidak menyediakan API tertentu untuk membuat atau memelihara tampilan, terwujud/terindeks atau sebaliknya; tetapi sangat baik-baik saja untuk membuat migrasi kosong dan menambahkan definisi tampilan melalui SQL mentah.

Pemetaan warisan

Disarankan untuk membaca halaman khusus tentang warisan sebelum melanjutkan dengan bagian ini.

EF Core saat ini mendukung tiga teknik untuk memetakan model warisan ke database relasional:

  • Tabel per hierarki (TPH), di mana seluruh hierarki .NET kelas dipetakan ke tabel database tunggal.
  • Tabel per jenis (TPT), di mana setiap jenis dalam hierarki .NET dipetakan ke tabel lain dalam database.
  • Table-per-concrete-type (TPC), di mana setiap jenis beton dalam hierarki .NET dipetakan ke tabel yang berbeda dalam database, di mana setiap tabel berisi kolom untuk semua properti jenis yang sesuai.

Pilihan teknik pemetaan warisan dapat memiliki dampak yang cukup besar pada performa aplikasi - disarankan untuk mengukur dengan hati-hati sebelum berkomitmen pada pilihan.

Secara intuitif, TPT mungkin tampak seperti teknik "lebih bersih"; tabel terpisah untuk setiap jenis .NET membuat skema database terlihat mirip dengan hierarki jenis .NET. Selain itu, karena TPH harus mewakili seluruh hierarki dalam satu tabel, baris memiliki semua kolom terlepas dari jenis yang benar-benar ditahan dalam baris, dan kolom yang tidak terkait selalu kosong dan tidak digunakan. Selain tampak sebagai teknik pemetaan yang "tidak bersih", banyak yang percaya bahwa kolom kosong ini mengambil ruang yang cukup besar dalam database dan mungkin menyakiti performa juga.

Petunjuk / Saran

Jika sistem database Anda mendukungnya (e.g. SQL Server), maka pertimbangkan untuk menggunakan "kolom jarang" untuk kolom TPH yang jarang diisi.

Namun, pengukuran menunjukkan bahwa TPT dalam banyak kasus teknik pemetaan inferior dari sudut performa; di mana semua data dalam TPH berasal dari satu tabel, kueri TPT harus bergabung bersama beberapa tabel, dan gabungan adalah salah satu sumber utama masalah performa dalam database relasional. Database juga umumnya cenderung berurusan dengan baik dengan kolom kosong, dan fitur seperti kolom jarang SQL Server dapat mengurangi overhead ini lebih jauh.

TPC memiliki karakteristik performa yang sama dengan TPH, tetapi sedikit lebih lambat saat memilih entitas dari semua jenis karena ini melibatkan beberapa tabel. Namun, TPC benar-benar unggul saat mengkueri entitas dari jenis daun tunggal - kueri hanya menggunakan satu tabel dan tidak memerlukan pemfilteran.

Untuk contoh konkret, lihat tolok ukur ini yang menyiapkan model sederhana dengan hierarki 7 jenis; 5000 baris diunggulkan untuk setiap jenis - berjumlah 35000 baris - dan tolok ukur hanya memuat semua baris dari database:

Metode Rata-rata Kesalahan StdDev Gen 0 Gen 1 Dialokasikan
TPH 149,0 mdtk 3,38 mdtk 9,80 mdtk 4000.0000 1000.0000 40 MB
TPT 312,9 mdtk 6,17 mdtk 10,81 mdtk 9000.0000 3000.0000 75 MB
TPC 158,2 mdtk 3,24 mdtk 8,88 mdtk 5000.0000 2000.0000 46 MB

Seperti yang dapat dilihat, TPH dan TPC jauh lebih efisien daripada TPT untuk skenario ini. Perhatikan bahwa hasil aktual selalu bergantung pada kueri tertentu yang dijalankan dan jumlah tabel dalam hierarki, sehingga kueri lain mungkin menunjukkan kesenjangan performa yang berbeda; Anda dianjurkan untuk menggunakan kode tolok ukur ini sebagai templat untuk menguji kueri lain.