2016-09-02 64 views
11

Trong một kịch bản nhiều luồng, tôi gặp vấn đề với truy vấn EF cụ thể. Nó thường rẻ và nhanh chóng:Sự chậm trễ nghiêm trọng của EF và SQL

Context.MyEntity 
    .Any(se => se.SameEntity.Field == someValue   
    && se.AnotherEntity.Field == anotherValue 
    && se.SimpleField == simpleValue 
    // few more simple predicates with fields on the main entity 
    ); 

này biên dịch thành một truy vấn SQL rất hợp lý:

SELECT 
CASE WHEN (EXISTS (SELECT 
    1 AS [C1] 
    FROM (SELECT [Extent1].[Field1] AS [Field1] 
     FROM [dbo].[MyEntity] AS [Extent1] 
     INNER JOIN [dbo].[SameEntity] AS [Extent2] ON [Extent1].[SameEntity_Id] = [Extent2].[Id] 
     WHERE (N'123' = [Extent2].[SimpleField]) AND (123 = [Extent1].[AnotherEntity_Id]) AND -- further simple predicates here --) AS [Filter1] 
    INNER JOIN [dbo].[AnotherEntity] AS [Extent3] ON [Filter1].[AnotherEntity_Id1] = [Extent3].[Id] 
    WHERE N'123' = [Extent3].[SimpleField] 
)) THEN cast(1 as bit) ELSE cast(0 as bit) END AS [C1] 
FROM (SELECT 1 AS X) AS [SingleRowTable1] 

Truy vấn, nói chung, có kế hoạch truy vấn tối ưu, sử dụng các chỉ số đúng và lợi nhuận trong hàng chục mili giây hoàn toàn có thể chấp nhận được.

Tuy nhiên, khi một số chuỗi chủ đề quan trọng (< = 40) bắt đầu thực hiện truy vấn này, hiệu suất trên nó xuống đến hàng chục giây.

Không có khóa trong cơ sở dữ liệu, không có truy vấn nào đang ghi dữ liệu vào các bảng này và nó tái tạo rất tốt với cơ sở dữ liệu được phân tách thực tế từ bất kỳ hoạt động nào khác. DB nằm trên cùng một máy vật lý và máy không bị quá tải tại bất kỳ điểm nào, nghĩa là có nhiều CPU dự phòng, bộ nhớ và các tài nguyên khác CPU bị quá tải bởi thao tác này.

Điều thực sự kỳ lạ là khi tôi thay thế cuộc gọi EF Any() bằng Context.Database.ExecuteSqlCommand() bằng SQL được sao chép (cũng sử dụng các tham số), vấn đề biến mất một cách kỳ diệu. Một lần nữa, điều này tái tạo rất đáng tin cậy - thay thế cuộc gọi Any() với SQL được sao chép sẽ tăng hiệu suất theo 2-3 đơn đặt hàng của độ lớn.


Một hồ sơ đính kèm (dotTrace) lấy mẫu cho thấy rằng các chủ đề dường như tất cả dành thời gian của họ bằng phương pháp sau:

dotTrace sample

Có điều gì tôi đã bỏ lỡ hoặc chúng tôi đã trúng số ADO.NET/SQL Server cornercase?


bối cảnh nhiều hơn

Mã chạy truy vấn này là một công việc Hangfire. Với mục đích thử nghiệm, một tập lệnh xếp hàng rất nhiều công việc cần thực hiện và tối đa 40 chủ đề tiếp tục xử lý công việc. Mỗi công việc sử dụng một cá thể DbContext riêng biệt và nó không thực sự được sử dụng nhiều. Có thêm một vài truy vấn trước và sau truy vấn có vấn đề và chúng mất thời gian dự kiến ​​để thực thi.

Chúng tôi đang sử dụng nhiều công việc Hangfire khác nhau cho các mục đích tương tự và chúng hoạt động như mong đợi. Tương tự như thế này, trừ khi nó bị chậm dưới sự đồng thời cao (của cùng một công việc chính xác). Ngoài ra, chỉ cần chuyển sang SQL trên truy vấn cụ thể này sẽ khắc phục được sự cố.

Ảnh chụp nhanh hồ sơ ở trên là đại diện, tất cả các chuỗi đều làm chậm cuộc gọi phương thức cụ thể này và dành phần lớn thời gian của chúng trên đó.


CẬP NHẬT

tôi đang chạy lại rất nhiều những người kiểm tra cho sự tỉnh táo và các lỗi. Việc sao chép dễ dàng có nghĩa là nó vẫn còn trên một máy từ xa mà tôi không thể kết nối bằng cách sử dụng VS để gỡ lỗi.

Một trong các kiểm tra cho thấy rằng câu lệnh trước của tôi về CPU tự do là sai, CPU không hoàn toàn quá tải nhưng nhiều lõi trên thực tế đang chạy hết công suất trong suốt thời gian dài.

Kiểm tra lại mọi thứ một lần nữa và sẽ quay lại với nội dung cập nhật tại đây.

+0

Phương thức đó chỉ chờ máy chủ trả lời. Nó không phải là lỗi. (Tôi không biết là gì.) – usr

+0

Tại thời điểm chậm, hãy tạm dừng trình gỡ lỗi và chụp ảnh chụp màn hình cửa sổ Parallel Stacks. Chúng ta cần một mẫu những gì các chủ đề đang làm. – usr

+0

Như đã nêu ở trên, trình bày hồ sơ đường dẫn nóng - tất cả các chủ đề chậm lại trên phương pháp này. Tôi nghi ngờ một trong hai trường hợp góc trong kết nối SQL hoặc có thể là một loạt các sự kiện gây ra SQL chính nó để chặn/làm chậm (ngay cả với các nguồn tài nguyên miễn phí). –

Trả lời

1

Giả định ban đầu bị lỗi. SQL trong câu hỏi thu được bằng cách dán mã vào LINQPad và có nó tạo ra SQL.

Sau khi đính kèm một lược tả SQL vào DB thực tế được sử dụng, nó cho thấy SQL hơi khác nhau SQL liên quan đến các kết nối bên ngoài, không tối ưu và không có chỉ mục phù hợp.

Nó vẫn còn là một bí ẩn lý do tại sao LINQPad tạo SQL khác nhau, mặc dù nó sử dụng cùng một EntityFramework.dll, nhưng vấn đề ban đầu được giải quyết và tất cả những gì còn lại là tối ưu hóa truy vấn.

Cảm ơn nhiều người đã tham gia.

1

Bạn có thể thử như hình dưới đây và xem liệu có bất kỳ cải thiện hiệu suất hay không ...

Context.MyEntity.AsNoTracking() 
    .Any(se => se.SameEntity.Field == someValue   
    && se.AnotherEntity.Field == anotherValue 
    && se.SimpleField == simpleValue 
    ); 
+0

'' Bất kỳ() '' trả về một bool trong khi '' AsNoTracking() '' là một phương thức mở rộng trên '' IQueryable'' ... –

+0

Tôi đã cập nhật câu trả lời.Vui lòng xem bây giờ. – Sampath

+0

AsNoTracking() đã được thử trước đó và không có hiệu lực. Trong thực tế, truy vấn SQL trả về chỉ một số, do đó, theo dõi không nên chịu trách nhiệm (trong thế giới hợp lý nào). –

1

Kiểm tra nếu bạn đang sử dụng lại bối cảnh trong một vòng lặp. Làm như vậy có thể tạo ra nhiều đối tượng trong quá trình kiểm tra hiệu năng của bạn và cho người thu gom rác nhiều việc phải làm.

Các vấn đề liên quan