5

特定の順序で読み取り、集計統計を計算する必要がある巨大なテーブルがあります。テーブルには正しい順序のクラスター化インデックスが既にあるため、レコード自体の取得は非常に高速です。LINQ to SQL を使用して、記述する必要があるコードを簡素化しようとしています。問題は、DataContext がオブジェクトを保持しているように見えるため、すべてのオブジェクトをメモリにロードしたくないということです。

これが内訳です。元の試みはこれでした:

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 many 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があります。基礎となるクエリが実際にサーバーに前のすべてのページのすべてのデータを通過させるように見えるため、データの読み込みがますます遅くなります。クエリを実行している間、各ページの読み込みに時間がかかり、これが 2 次演算になっていることがわかります。この問題は、次の投稿に表示されています。

データのページングによるメモリの使用を制限しながら、各ページを一定時間でロードできるようにするLINQでこれを行う方法を見つけることができないようです。これを適切に行う方法はありますか?上記の最初のアプローチでオブジェクトを明示的に忘れるように DataContext に指示する方法があるかもしれませんが、その方法がわかりません。

4

1 に答える 1

16

DataContext狂ったようにいくつかのストローをつかんだ後、私はそれObjectTrackingEnabled = 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 クエリのストリーミングと完了に 1 時間以上かかるという問題があるため、パフォーマンスに影響を与えずに上記のようにページングを行う方法があれば、他の方法を検討します。

オブジェクト追跡をオフにすることに関する警告: 同じ で複数の同時読み取りを実行しようとするとDataContext、エラーが発生しないことがわかりましたThere is already an open DataReader associated with this Command which must be closed first.アプリケーションは 100% の CPU 使用率で無限ループに入ります。これが C# のバグなのか機能なのかはわかりません。

于 2012-09-17T23:20:30.983 に答える