Sqlserver
 sql >> Teknologi Basis Data >  >> RDS >> Sqlserver

Kode kerangka kerja entitas lambat saat menggunakan Include() berkali-kali

tl;dr Beberapa Include s meledakkan set hasil SQL. Segera menjadi lebih murah untuk memuat data dengan beberapa panggilan basis data daripada menjalankan satu pernyataan besar. Cobalah untuk menemukan campuran terbaik dari Include dan Load pernyataan.

sepertinya ada penalti kinerja saat menggunakan Sertakan

Itu pernyataan yang meremehkan! Beberapa Include s dengan cepat meledakkan hasil kueri SQL baik lebar maupun panjangnya. Mengapa demikian?

Faktor pertumbuhan Include s

(Bagian ini menerapkan Entity Framework klasik, v6 dan sebelumnya)

Katakanlah kita memiliki

  • entitas root Root
  • entitas induk Root.Parent
  • entitas anak Root.Children1 dan Root.Children2
  • pernyataan LINQ Root.Include("Parent").Include("Children1").Include("Children2")

Ini membangun pernyataan SQL yang memiliki struktur berikut:

SELECT *, <PseudoColumns>
FROM Root
JOIN Parent
JOIN Children1

UNION

SELECT *, <PseudoColumns>
FROM Root
JOIN Parent
JOIN Children2

Ini <PseudoColumns> terdiri dari ekspresi seperti CAST(NULL AS int) AS [C2], dan mereka berfungsi untuk memiliki jumlah kolom yang sama di semua UNION -ed pertanyaan. Bagian pertama menambahkan kolom semu untuk Child2 , bagian kedua menambahkan kolom semu untuk Child1 .

Inilah yang dimaksud dengan ukuran set hasil SQL:

  • Jumlah kolom di SELECT klausa adalah jumlah semua kolom dalam empat tabel
  • Jumlah baris adalah jumlah record dalam koleksi anak yang disertakan

Karena jumlah total titik data adalah columns * rows , setiap tambahan Include secara eksponensial meningkatkan jumlah total titik data dalam kumpulan hasil. Biarkan saya menunjukkannya dengan mengambil Root lagi, sekarang dengan tambahan Children3 koleksi. Jika semua tabel memiliki 5 kolom dan 100 baris, kita peroleh:

Satu Include (Root + 1 koleksi anak):10 kolom * 100 baris =1000 titik data.
Dua Include s (Root + 2 koleksi anak):15 kolom * 200 baris =3000 titik data.
Tiga Include s (Root + 3 koleksi anak):20 kolom * 300 baris =6000 titik data.

Dengan 12 Includes ini akan berjumlah 78000 poin data!

Sebaliknya, jika Anda mendapatkan semua catatan untuk setiap tabel secara terpisah, bukan 12 Includes , Anda memiliki 13 * 5 * 100 poin data:6500, kurang dari 10%!

Sekarang angka-angka ini agak dilebih-lebihkan karena banyak dari titik data ini akan menjadi null , jadi mereka tidak berkontribusi banyak pada ukuran sebenarnya dari kumpulan hasil yang dikirim ke klien. Tetapi ukuran kueri dan tugas untuk pengoptimal kueri tentu saja terpengaruh secara negatif dengan meningkatnya jumlah Include s.

Saldo

Jadi menggunakan Includes adalah keseimbangan antara biaya panggilan database dan volume data. Sulit untuk memberikan aturan praktis, tetapi sekarang Anda dapat membayangkan bahwa volume data umumnya dengan cepat melebihi biaya panggilan tambahan jika ada lebih dari ~3 Includes untuk koleksi anak (tetapi sedikit lebih banyak untuk induk Includes , yang hanya memperlebar kumpulan hasil).

Alternatif

Alternatif untuk Include adalah memuat data dalam kueri terpisah:

context.Configuration.LazyLoadingEnabled = false;
var rootId = 1;
context.Children1.Where(c => c.RootId == rootId).Load();
context.Children2.Where(c => c.RootId == rootId).Load();
return context.Roots.Find(rootId);

Ini memuat semua data yang diperlukan ke dalam cache konteks. Selama proses ini, EF menjalankan perbaikan hubungan yang secara otomatis mengisi properti navigasi (Root.Children dll.) oleh entitas yang dimuat. Hasil akhirnya identik dengan pernyataan dengan Include s, kecuali untuk satu perbedaan penting:koleksi anak tidak ditandai sebagai dimuat di pengelola status entitas, jadi EF akan mencoba memicu pemuatan lambat jika Anda mengaksesnya. Itulah mengapa penting untuk menonaktifkan pemuatan lambat.

Pada kenyataannya, Anda harus mencari tahu kombinasi mana dari Include dan Load pernyataan paling cocok untuk Anda.

Aspek lain yang perlu dipertimbangkan

Setiap Include juga meningkatkan kompleksitas kueri, sehingga pengoptimal kueri basis data harus berupaya lebih keras untuk menemukan rencana kueri terbaik. Pada titik tertentu ini mungkin tidak lagi berhasil. Juga, ketika beberapa indeks penting tidak ada (khususnya pada kunci asing), kinerja mungkin terganggu dengan menambahkan Include s, bahkan dengan paket kueri terbaik.

Inti Kerangka Entitas

Ledakan Kartesius

Untuk beberapa alasan, perilaku yang dijelaskan di atas, kueri UNION, ditinggalkan pada EF core 3. Sekarang membuat satu kueri dengan gabungan. Ketika kueri berbentuk "bintang", ini mengarah ke ledakan Cartesian (dalam kumpulan hasil SQL). Saya hanya dapat menemukan catatan yang mengumumkan perubahan yang melanggar ini, tetapi tidak disebutkan alasannya.

Pisahkan kueri

Untuk mengatasi ledakan Cartesian ini, Entity Framework core 5 memperkenalkan konsep kueri terpisah yang memungkinkan memuat data terkait dalam beberapa kueri. Ini mencegah membangun satu set hasil SQL yang besar dan berlipat ganda. Selain itu, karena kompleksitas kueri yang lebih rendah, ini dapat mengurangi waktu yang diperlukan untuk mengambil data bahkan dengan beberapa perjalanan pulang pergi. Namun, ini dapat menyebabkan data yang tidak konsisten saat terjadi pembaruan secara bersamaan.

Beberapa hubungan 1:n keluar dari akar kueri.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Tambahkan hubungan Kunci Asing antara dua Basis Data

  2. Gunakan OBJECTPROPERTY() untuk Mengetahui apakah Tabel adalah Tabel Sistem di SQL Server

  3. Kembalikan Hak Istimewa Kolom dari Server Tertaut di SQL Server (Contoh T-SQL)

  4. SQL Server:Sisi gelap NVARCHAR

  5. ORDER BY DATE menampilkan NULLS terlebih dahulu kemudian tanggal terbaru