2012-09-17 31 views
5

Tôi có một bảng lớn mà tôi cần đọc qua một thứ tự nhất định và tính toán một số thống kê tổng hợp. Bảng đã có một chỉ số nhóm cho đúng thứ tự sao cho bản thân các bản ghi là khá nhanh. Tôi đang cố gắng sử dụng LINQ to SQL để đơn giản hóa mã mà tôi cần phải viết. Vấn đề là tôi không muốn tải tất cả các đối tượng vào bộ nhớ, vì DataContext dường như giữ chúng xung quanh - nhưng cố gắng để trang chúng kết quả trong các vấn đề hiệu suất khủng khiếp.Đọc bảng lớn với LINQ to SQL: Hết bộ nhớ và phân trang chậm

Đây là bảng phân tích. Nỗ lực ban đầu là:

var logs = 
    (from record in dataContext.someTable 
    where [index is appropriate] 
    select record); 

foreach(linqEntity l in logs) 
{ 
    // Do stuff with data from l 
} 

Điều này là khá nhanh và luồng ở mức tốt, nhưng vấn đề là việc sử dụng bộ nhớ của ứng dụng liên tục không ngừng. Tôi đoán là các thực thể LINQ to SQL đang được lưu trữ trong bộ nhớ và không được xử lý đúng cách. Vì vậy, sau khi đọc Out of memory when creating a lot of objects C#, tôi đã thử cách tiếp cận sau. Đây có vẻ là mô hình phổ biến của Skip/Take mà nhiều người sử dụng, với tính năng tiết kiệm bộ nhớ bổ sung.

Lưu ý rằng _conn được tạo trước và ngữ cảnh dữ liệu tạm thời được tạo cho mỗi truy vấn, dẫn đến các thực thể liên quan bị thu gom rác.

int skipAmount = 0; 
bool finished = false; 

while (!finished) 
{ 
    // Trick to allow for automatic garbage collection while iterating through the DB 
    using (var tempDataContext = new MyDataContext(_conn) {CommandTimeout = 600}) 
    {    
     var query = 
      (from record in tempDataContext.someTable 
      where [index is appropriate] 
      select record); 

     List<workerLog> logs = query.Skip(skipAmount).Take(BatchSize).ToList(); 
     if (logs.Count == 0) 
     { 
      finished = true; 
      continue; 
     } 

     foreach(linqEntity l in logs) 
     { 
      // Do stuff with data from l 
     } 

     skipAmount += logs.Count; 
    } 
} 

Bây giờ tôi có hành vi mong muốn rằng việc sử dụng bộ nhớ không tăng chút nào khi tôi đang truyền trực tuyến dữ liệu. Tuy nhiên, tôi có một vấn đề tồi tệ hơn: mỗi Skip đang làm cho dữ liệu tải ngày càng chậm vì truy vấn cơ bản dường như thực sự khiến máy chủ phải trải qua tất cả dữ liệu cho tất cả các trang trước đó. Trong khi chạy truy vấn mỗi trang mất nhiều thời gian hơn và lâu hơn để tải và tôi có thể nói rằng điều này đang chuyển thành hoạt động bậc hai. Vấn đề này đã xuất hiện trong các bài viết sau:

tôi dường như không thể tìm thấy một cách để làm điều này với LINQ cho phép tôi có sử dụng bộ nhớ hạn chế bởi phân trang dữ liệu, nhưng vẫn có tải trang mỗi lần liên tục. Có cách nào để làm điều này đúng không? Linh cảm của tôi là có thể có một số cách để báo cho DataContext biết rõ ràng về đối tượng trong cách tiếp cận đầu tiên ở trên, nhưng tôi không thể tìm ra cách để làm điều đó.

+0

"Tôi có một bảng lớn mà tôi cần phải đọc qua một thứ tự nhất định và tính toán một số thống kê tổng hợp." - Làm điều đó tại máy chủ trong TSQL .... Đó là những gì nó là tốt! –

+0

Không, các số liệu thống kê phức tạp hơn và không thể tính toán được với các truy vấn SQL. Dữ liệu cần được lặp lại thông qua một thứ tự nhất định và những thứ được tính toán đúng thời gian, v.v. –

+0

"Không, số liệu thống kê phức tạp hơn và không thể tính toán được với các truy vấn SQL" - thực sự? Có thể đưa ra một ví dụ đầy đủ không? –

Trả lời

15

Sau khi nắm bắt một cách điên cuồng vào một số ống hút, tôi thấy rằng's ObjectTrackingEnabled = false có thể chỉ là những gì bác sĩ đã ra lệnh. Nó là, không đáng ngạc nhiên, được thiết kế đặc biệt cho một trường hợp chỉ đọc như thế này.

using (var readOnlyDataContext = 
    new MyDataContext(_conn) {CommandTimeout = really_long, ObjectTrackingEnabled = false}) 
{             
    var logs = 
     (from record in readOnlyDataContext.someTable 
     where [index is appropriate] 
     select record); 

    foreach(linqEntity l in logs) 
    { 
     // Do stuff with data from l 
    }     
} 

Cách tiếp cận trên không sử dụng bất kỳ bộ nhớ nào khi phát trực tuyến qua các đối tượng. Khi ghi dữ liệu, tôi có thể sử dụng một số khác nhau DataContext đã bật tính năng theo dõi đối tượng và có vẻ như hoạt động tốt. Tuy nhiên, cách tiếp cận này có vấn đề về truy vấn SQL có thể mất một giờ hoặc hơn để truyền và hoàn thành, vì vậy nếu có cách để thực hiện phân trang như trên mà không có hiệu suất, tôi mở cho các lựa chọn thay thế khác.

Một cảnh báo về biến theo dõi tắt đối tượng: Tôi phát hiện ra rằng khi bạn cố gắng làm nhiều đồng thời đọc với cùng DataContext, bạn không nhận được lỗi There is already an open DataReader associated with this Command which must be closed first. Ứng dụng này chỉ đi vào một vòng lặp vô hạn với 100% Sử dụng CPU. Tôi không chắc đây có phải là lỗi C# hay tính năng.

+0

Bài đăng rất hữu ích! +2 – craastad

+0

Tôi có cùng một vấn đề, đọc theo hàng 1m và hết bộ nhớ. Với "ObjectTrackingEnabled" được đặt thành false, bộ sưu tập rác * đã * giải phóng bộ nhớ mà chức năng này đã sử dụng. Không có nó, khoảng 300 triệu bộ nhớ không được phát hành. Vì vậy, thiết lập nhỏ này có thể tạo ra một tác động rất lớn. –