Truy vấn Entity Framework chậm, nhưng SQL tương tự trong SqlQuery thì nhanh


93

Tôi thấy một số hiệu suất thực sự kỳ lạ liên quan đến một truy vấn rất đơn giản bằng cách sử dụng Entity Framework Code-First với .NET framework phiên bản 4. Truy vấn LINQ2Entities trông giống như sau:

 context.MyTables.Where(m => m.SomeStringProp == stringVar);

Quá trình này mất hơn 3000 mili giây để thực thi. SQL được tạo trông rất đơn giản:

 SELECT [Extent1].[ID], [Extent1].[SomeStringProp], [Extent1].[SomeOtherProp],
 ...
 FROM [MyTable] as [Extent1]
 WHERE [Extent1].[SomeStringProp] = '1234567890'

Truy vấn này chạy gần như ngay lập tức khi chạy qua Management Studio. Khi tôi thay đổi mã C # để sử dụng hàm SqlQuery, nó sẽ chạy trong 5-10 mili giây:

 context.MyTables.SqlQuery("SELECT [Extent1].[ID] ... WHERE [Extent1].[SomeStringProp] = @param", stringVar);

Vì vậy, cùng một SQL chính xác, các thực thể kết quả được theo dõi thay đổi trong cả hai trường hợp, nhưng có sự khác biệt hoàn toàn giữa hai trường hợp. Đưa cái gì?


2
Tôi hy vọng bạn đang thấy sự chậm trễ trong quá trình khởi tạo - có thể là xem quá trình biên dịch. Xem MSDN:Performance Considerations for Entity Framework 5
Nicholas Butler,

Tôi đã thử tạo trước lượt xem và nó dường như không giúp được gì. Ngoài ra, chạy một truy vấn EF khác trước truy vấn chậm để loại trừ nội dung khởi tạo. Truy vấn mới chạy nhanh, truy vấn chậm vẫn chạy chậm, ngay cả khi khởi động ngữ cảnh đã xảy ra trong lần truy vấn đầu tiên.
Brian Sullivan

1
@marc_s - Không, SqlQuery sẽ trả về một cá thể thực thể được theo dõi thay đổi và được vật liệu hóa đầy đủ. Xem msdn.microsoft.com/en-us/library/…
Brian Sullivan

SQL được tạo cho truy vấn EF của bạn có thực sự nội dòng giá trị tham số hay sử dụng tham số không? Điều này sẽ không ảnh hưởng đến tốc độ truy vấn cho một truy vấn riêng lẻ, nhưng có thể gây ra tình trạng phồng lên sơ đồ truy vấn trong máy chủ theo thời gian.
Jim Wooley

Bạn đã thử chạy cùng một truy vấn hai lần / nhiều lần chưa? Hỏi khi chạy lần thứ hai đã mất bao lâu? Bạn đã thử điều này trên .NET Framework 4.5 chưa - có một số cải tiến hiệu suất liên quan đến EF trong .NET Framework 4.5 có thể giúp ích.
Pawel

Câu trả lời:


96

Tìm thấy rồi. Hóa ra đó là vấn đề về kiểu dữ liệu SQL. Các SomeStringPropcột trong cơ sở dữ liệu là một varchar, nhưng EF giả định rằng NET loại chuỗi là nvarchars. Quá trình dịch kết quả trong quá trình truy vấn để DB thực hiện so sánh là một việc mất nhiều thời gian. Tôi nghĩ rằng Giáo sư EF đã dẫn tôi đi lạc một chút ở đây, phần trình bày chính xác hơn về truy vấn đang được chạy sẽ như sau:

 SELECT [Extent1].[ID], [Extent1].[SomeStringProp], [Extent1].[SomeOtherProp],
 ...
 FROM [MyTable] as [Extent1]
 WHERE [Extent1].[SomeStringProp] = N'1234567890'

Vì vậy, bản sửa lỗi kết quả là chú thích mô hình đầu tiên mã, chỉ ra kiểu dữ liệu SQL chính xác:

public class MyTable
{
    ...

    [Column(TypeName="varchar")]
    public string SomeStringProp { get; set; }

    ...
}

1
Điều tra tốt đẹp. Truy vấn của bạn bị "chuyển đổi ngầm định", như nó được giải thích ở đây: brentozar.com/archive/2012/07/…
Jaime

Đã tiết kiệm cho tôi một vài giờ gỡ lỗi. Đây chính xác là vấn đề.
Cody

1
Trong trường hợp của tôi, tôi đang sử dụng EDMX với cơ sở dữ liệu kế thừa, sử dụng varcharcho mọi thứ và thực sự đây là vấn đề. Tôi tự hỏi liệu tôi có thể tạo một EDMX để xem xét varchar cho cột chuỗi mọi thứ hay không.
Alisson

1
Người đàn ông tìm kiếm tuyệt vời. nhưng @Jaime những gì chúng ta nên làm đối với cách tiếp cận cơ sở dữ liệu đầu tiên vì mọi thứ (ví dụ: chú thích dữ liệu trên Mô hình db) bị xóa sạch sau khi cập nhật mô hình EF từ cơ sở dữ liệu.
Nauman Khan,

Đặt trang này làm trang chủ của tôi trong một thời gian để tôi có thể hồi tưởng lại sự phấn khích khi tìm lại được câu trả lời tuyệt vời như vậy trong một thời gian. Cảm ơn bạn!!!
OJisBad

43

Lý do làm chậm các truy vấn của tôi được thực hiện trong EF là so sánh các đại lượng vô hướng không null với vô hướng vô hướng:

long? userId = 10; // nullable scalar

db.Table<Document>().Where(x => x.User.Id == userId).ToList() // or userId.Value
                                ^^^^^^^^^    ^^^^^^
                                Type: long   Type: long?

Truy vấn đó mất 35 giây. Nhưng một cấu trúc lại nhỏ như vậy:

long? userId = 10;
long userIdValue = userId.Value; // I've done that only for the presentation pursposes

db.Table<Document>().Where(x => x.User.Id == userIdValue).ToList()
                                ^^^^^^^^^    ^^^^^^^^^^^
                                Type: long   Type: long

cho kết quả đáng kinh ngạc. Nó chỉ mất 50ms để hoàn thành. Có thể đó là một lỗi trong EF.


13
Điều này là rất kỳ lạ
Daniel Cardenas

1
CHÚA ƠI. Điều này dường như cũng có thể xảy ra khi sử dụng giao diện IUserId.Id đã gây ra sự cố với tôi, nhưng ánh xạ Id đầu tiên với một số nguyên hoạt động ... tôi có phải kiểm tra tất cả các truy vấn trong ứng dụng 100.000 dòng của mình ngay bây giờ không?
Dirk Boer

lỗi này đã được báo cáo chưa? Nó vẫn còn trong phiên bản mới nhất 6.2.0
Dirk Boer

2
Vấn đề tương tự cũng xảy ra trong EF Core. Cảm ơn vì đã tìm thấy điều này!
Yannickv

Một gợi ý khác là xử lý biến trước khi đưa vào biểu thức LINQ. Nếu không, sql được tạo sẽ lâu hơn và chậm hơn nhiều. Tôi đã trải nghiệm khi có Trim () và ToLower () bên trong biểu thức LINQ khiến tôi khó chịu.
samheihey


4

Tôi đã gặp vấn đề tương tự (truy vấn nhanh khi được thực thi từ trình quản lý SQL) nhưng khi thực thi từ EF thì hết thời gian chờ.

Hóa ra thực thể (được tạo từ chế độ xem) có khóa thực thể sai. Vì vậy, thực thể có các hàng trùng lặp với các khóa giống nhau và tôi đoán nó phải thực hiện nhóm trên nền.


3

Tôi cũng đã gặp điều này với một truy vấn ef phức tạp. Một bản sửa lỗi cho tôi đã giảm truy vấn ef 6 giây xuống truy vấn sql thứ hai mà nó tạo ra là tắt tải chậm.

Để tìm cài đặt này (ef 6), hãy truy cập tệp .edmx và tìm trong Thuộc tính -> Tạo mã -> Đã bật tải chậm. Đặt thành false.

Cải thiện đáng kể hiệu suất cho tôi.


4
Điều đó thật tuyệt, nhưng không liên quan gì đến câu hỏi về áp phích.
Jace Rhea

2

Tôi cũng có vấn đề này. Hóa ra thủ phạm trong trường hợp của tôi là đánh hơi tham số SQL-Server .

Manh mối đầu tiên mà vấn đề của tôi trên thực tế là do đánh hơi tham số là chạy truy vấn với "set arithabort off" hoặc "set arithabort on" mang lại thời gian thực thi khác nhau đáng kể trong Management Studio. Điều này là do ADO.NET theo mặc định sử dụng "set arithabort off" và Management Studio mặc định là "set arithabort on". Bộ đệm ẩn kế hoạch truy vấn giữ các kế hoạch khác nhau tùy thuộc vào tham số này.

Tôi đã tắt bộ nhớ đệm kế hoạch truy vấn cho truy vấn, với giải pháp bạn có thể tìm thấy tại đây .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.