特定の順序で読み取り、集計統計を計算する必要がある巨大なテーブルがあります。テーブルには正しい順序のクラスター化インデックスが既にあるため、レコード自体の取得は非常に高速です。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 に指示する方法があるかもしれませんが、その方法がわかりません。