Sqlserver
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Sqlserver

Mã khung thực thể chậm khi sử dụng Bao gồm () nhiều lần

tl; dr Nhiều Include s làm nổ tập kết quả SQL. Việc tải dữ liệu bằng nhiều lệnh gọi cơ sở dữ liệu sẽ sớm trở nên rẻ hơn thay vì chạy một câu lệnh lớn. Cố gắng tìm hỗn hợp tốt nhất của IncludeLoad tuyên bố.

có vẻ như có một hình phạt hiệu suất khi sử dụng Bao gồm

Đó là một cách nói ngắn gọn! Nhiều Include s nhanh chóng làm nổ tung kết quả truy vấn SQL cả về chiều rộng và chiều dài. Tại sao vậy?

Yếu tố tăng trưởng của Include s

(Phần này áp dụng Entity Framework classic, v6 trở về trước)

Giả sử chúng ta có

  • thực thể gốc Root
  • thực thể mẹ Root.Parent
  • các thực thể con Root.Children1Root.Children2
  • câu lệnh LINQ Root.Include("Parent").Include("Children1").Include("Children2")

Điều này xây dựng một câu lệnh SQL có cấu trúc sau:

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

UNION

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

<PseudoColumns> này bao gồm các biểu thức như CAST(NULL AS int) AS [C2], và chúng có cùng số lượng cột trong tất cả UNION truy vấn -ed. Phần đầu tiên thêm các cột giả cho Child2 , phần thứ hai thêm các cột giả cho Child1 .

Đây là ý nghĩa của nó đối với kích thước của tập kết quả SQL:

  • Số lượng cột trong SELECT mệnh đề là tổng của tất cả các cột trong bốn bảng
  • Số lượng hàng là tổng các bản ghi trong các tập hợp con được bao gồm

Vì tổng số điểm dữ liệu là columns * rows , mỗi Include Tăng tổng số điểm dữ liệu trong tập kết quả theo cấp số nhân. Hãy để tôi chứng minh điều đó bằng cách lấy Root một lần nữa, bây giờ có thêm Children3 thu thập. Nếu tất cả các bảng có 5 cột và 100 hàng, chúng ta nhận được:

Một Include (Root + 1 tập hợp con):10 cột * 100 hàng =1000 điểm dữ liệu.
Hai Include s (Root + 2 tập hợp con):15 cột * 200 hàng =3000 điểm dữ liệu.
Ba Include s (Root + 3 tập hợp con):20 cột * 300 hàng =6000 điểm dữ liệu.

Với 12 Include con số này sẽ lên tới 78000 điểm dữ liệu!

Ngược lại, nếu bạn nhận được tất cả các bản ghi cho từng bảng riêng biệt thay vì 12 Include , bạn có 13 * 5 * 100 điểm dữ liệu:6500, ít hơn 10%!

Giờ đây, những con số này hơi phóng đại khi nhiều điểm dữ liệu này sẽ là null , vì vậy chúng không đóng góp nhiều vào kích thước thực của tập kết quả được gửi đến máy khách. Nhưng kích thước truy vấn và nhiệm vụ cho trình tối ưu hóa truy vấn chắc chắn bị ảnh hưởng tiêu cực bởi số lượng Include ngày càng tăng s.

Số dư

Vì vậy, sử dụng Include là một sự cân bằng tinh tế giữa chi phí của các cuộc gọi cơ sở dữ liệu và khối lượng dữ liệu. Thật khó để đưa ra một quy tắc chung, nhưng bây giờ bạn có thể tưởng tượng rằng khối lượng dữ liệu thường nhanh chóng tăng nhanh hơn chi phí của các cuộc gọi bổ sung nếu có nhiều hơn ~ 3 Include cho các bộ sưu tập con (nhưng nhiều hơn một chút cho bộ sưu tập gốc Include , điều đó chỉ mở rộng tập kết quả).

Thay thế

Thay thế cho Include là tải dữ liệu trong các truy vấn riêng biệt:

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);

Điều này tải tất cả dữ liệu cần thiết vào bộ nhớ cache của ngữ cảnh. Trong quá trình này, EF thực thi sửa chữa mối quan hệ theo đó nó tự động điền các thuộc tính điều hướng (Root.Children vv) bởi các thực thể được tải. Kết quả cuối cùng giống với câu lệnh có Include s, ngoại trừ một điểm khác biệt quan trọng:các tập hợp con không được đánh dấu là đã tải trong trình quản lý trạng thái thực thể, vì vậy EF sẽ cố gắng kích hoạt tải chậm nếu bạn truy cập chúng. Đó là lý do tại sao điều quan trọng là phải tắt tải chậm.

Trong thực tế, bạn sẽ phải tìm ra sự kết hợp nào của IncludeLoad câu lệnh phù hợp nhất với bạn.

Các khía cạnh khác cần xem xét

Mỗi Include cũng làm tăng độ phức tạp của truy vấn, vì vậy trình tối ưu hóa truy vấn của cơ sở dữ liệu sẽ ngày càng phải nỗ lực nhiều hơn để tìm ra phương án truy vấn tốt nhất. Tại một số điểm, điều này có thể không còn thành công nữa. Ngoài ra, khi một số chỉ mục quan trọng bị thiếu (đặc biệt là khóa ngoại), hiệu suất có thể bị ảnh hưởng bằng cách thêm Include s, ngay cả với kế hoạch truy vấn tốt nhất.

Lõi khung thực thể

Vụ nổ Descartes

Vì một số lý do, hành vi được mô tả ở trên, các truy vấn UNIONed, đã bị bỏ qua đối với EF core 3. Giờ đây, nó xây dựng một truy vấn với các phép nối. Khi truy vấn có hình dạng "ngôi sao", điều này dẫn đến sự bùng nổ Descartes (trong tập kết quả SQL). Tôi chỉ có thể tìm thấy một ghi chú thông báo về sự thay đổi đột ngột này, nhưng nó không cho biết lý do tại sao.

Truy vấn phân tách

Để chống lại sự bùng nổ Descartes này, Entity Framework core 5 đã giới thiệu khái niệm truy vấn phân tách cho phép tải dữ liệu liên quan trong nhiều truy vấn. Nó ngăn cản việc xây dựng một tập kết quả SQL khổng lồ, được nhân lên. Ngoài ra, do độ phức tạp của truy vấn thấp hơn, nó có thể giảm thời gian tìm nạp dữ liệu ngay cả với nhiều vòng lặp. Tuy nhiên, nó có thể dẫn đến dữ liệu không nhất quán khi các cập nhật đồng thời xảy ra.

Nhiều mối quan hệ 1:n ngoài gốc truy vấn.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Cách đổi tên bảng trong SQL Server

  2. Làm cách nào để bạn thêm một cột NOT NULL vào một bảng lớn trong SQL Server?

  3. Cách khắc phục “Chức năng phân vùng được liên kết tạo ra nhiều phân vùng hơn số nhóm tệp được đề cập trong lược đồ” Msg 7707 trong SQL Server

  4. Nhật ký giao dịch SQL Server, Phần 3:Khái niệm cơ bản về ghi nhật ký

  5. Sự cố khi mở tệp MDF vì nó báo lỗi SQL 5171? - Bài đăng của khách của Andre Williams