2012-09-17 17 views
5

我有一個巨大的表,我需要通過一定的順序讀取和計算一些彙總統計信息。該表已經有正確順序的聚集索引,因此獲取記錄本身非常快。我正在嘗試使用LINQ to SQL來簡化我需要編寫的代碼。問題是我不想將所有對象加載到內存中,因爲DataContext似乎將它們放在了一邊 - 但試圖將它們分頁會導致可怕的性能問題。閱讀巨大的表的LINQ to SQL:運行內存VS慢分頁

這裏的擊穿。原始嘗試是這樣的:

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

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

這是相當快,並以良好的速度流,但問題是內存使用應用程序的繼續上升從未停止。我的猜測是,LINQ to SQL實體正在存儲在內存中,沒有正確處理。所以在閱讀Out of memory when creating a lot of objects C#後,我嘗試了以下方法。這似乎是常見的Skip/Take範例,許多人使用,以節省內存的附加功能。

注意_conn預先創建的,併爲每個查詢創建臨時數據上下文,從而在相關聯的實體被垃圾收集。

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

現在我有理想的行爲,因爲我通過數據進行流式傳輸時內存使用率沒有增加。不過,我有一個差遠了問題:每個Skip導致作爲基礎查詢,似乎確實導致服務器要經過所有的前幾頁的所有數據將數據加載越來越慢。在運行查詢時,每個頁面加載的時間越來越長,我可以判斷這是一個二次運算。這個問題已經出現在以下職位:

我似乎無法找到一種方法與LINQ做到這一點,讓我有被尋呼有限的內存使用數據,但每個頁面仍然在不斷的加載。有沒有辦法正確地做到這一點? 我的預感是,有可能是一些方法來告訴DataContext的明確忘記在上面的第一種方法的對象,但我不能找出如何做到這一點。

+0

「我有一個巨大的表,我需要通過一定的順序讀取和計算一些彙總統計信息。」 - 在TSQL的服務器做吧....這就是它擅長! –

+0

不,統計信息比這更復雜,並且不能用SQL查詢進行計算。數據需要按照一定的順序迭代,並且計算的東西在時間上是正確的,等等。 –

+0

「不,統計信息比這更復雜,而且不能用SQL查詢進行計算」 - 真的嗎?是否可以舉一個完整的例子? –

回答

15

在瘋狂地抓住一些吸管之後,我發現DataContextObjectTrackingEnabled = false可能正是醫生的命令。毫不奇怪,它是專門爲這種只讀情況設計的。

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 
    }     
} 

上述方法在通過對象進行流式傳輸時不使用任何內存。在編寫數據時,我可以使用啓用了對象跟蹤的不同DataContext,而且這似乎可以正常工作。然而,這種方法確實存在一個SQL查詢的問題,可能需要一個小時或更多的時間才能完成並傳輸,因此如果有一種方法可以在不影響性能的情況下進行分頁,我可以選擇其他方法。

約轉彎目標跟蹤關閉警告:我發現,當你嘗試做多併發使用相同的DataContext讀取,你沒有得到錯誤There is already an open DataReader associated with this Command which must be closed first.的應用剛剛進入一個無限循環使用100% CPU使用率。我不確定這是一個C#bug還是一個功能。

+0

非常有用的帖子! +2 – craastad

+0

我有完全相同的問題,以1m行讀取,並且內存不足。當「ObjectTrackingEnabled」設置爲false時,垃圾回收*會釋放此函數使用的內存。沒有它,大約300Mb的內存沒有被釋放。所以這個小設置可以產生巨大的影響。 –