Ich muss wie 1M Entitäten verarbeiten, um Fakten zu erstellen. Es sollte etwa die gleiche Menge an resultierenden Fakten geben (1 Million).Speicher wächst unerwartet während Entity Framework zu Massen einfügen
Das erste Problem, das ich hatte, war die Masseneinfügung es war langsam mit Entity Framework. Also habe ich dieses Muster verwendet Fastest Way of Inserting in Entity Framework (Antwort von SLauma). Und ich kann in einer Minute jetzt wirklich schnell 100K einfügen.
Ein anderes Problem, das ich fand, ist der Mangel an Speicher, um alles zu verarbeiten. Also habe ich die Verarbeitung "ausgelagert". Um eine Ausnahmebedingung zu vermeiden, würde ich bekommen, wenn ich eine Liste meiner 1 Million resultierenden Fakten mache.
Das Problem, das ich habe, ist, dass die Erinnerung immer mit dem Paging wächst und ich verstehe nicht warum. Nach jedem Batch wird kein Speicher freigegeben. Ich denke, das ist komisch, weil ich Fakten rekonstruieren und sie bei jeder Iteration der Schleife in der Datenbank ablege. Sobald die Schleife abgeschlossen ist, sollten diese aus dem Speicher freigegeben werden. Aber es sieht nicht so aus, weil nach jeder Iteration kein Speicher freigegeben wird.
Können Sie mir bitte sagen, wenn Sie etwas falsch sehen, bevor ich mehr graben? Genauer gesagt, warum nach einer Iteration der while-Schleife kein Speicher freigegeben wird.
static void Main(string[] args)
{
ReceiptsItemCodeAnalysisContext db = new ReceiptsItemCodeAnalysisContext();
var recon = db.Recons
.Where(r => r.Transacs.Where(t => t.ItemCodeDetails.Count > 0).Count() > 0)
.OrderBy(r => r.ReconNum);
// used for "paging" the processing
var processed = 0;
var total = recon.Count();
var batchSize = 1000; //100000;
var batch = 1;
var skip = 0;
var doBatch = true;
while (doBatch)
{ // list to store facts processed during the batch
List<ReconFact> facts = new List<ReconFact>();
// get the Recon items to process in this batch put them in a list
List<Recon> toProcess = recon.Skip(skip).Take(batchSize)
.Include(r => r.Transacs.Select(t => t.ItemCodeDetails))
.ToList();
// to process real fast
Parallel.ForEach(toProcess, r =>
{ // processing a recon and adding the facts to the list
var thisReconFacts = ReconFactGenerator.Generate(r);
thisReconFacts.ForEach(f => facts.Add(f));
Console.WriteLine(processed += 1);
});
// saving the facts using pattern provided by Slauma
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, new System.TimeSpan(0, 15, 0)))
{
ReceiptsItemCodeAnalysisContext context = null;
try
{
context = new ReceiptsItemCodeAnalysisContext();
context.Configuration.AutoDetectChangesEnabled = false;
int count = 0;
foreach (var fact in facts.Where(f => f != null))
{
count++;
Console.WriteLine(count);
context = ContextHelper.AddToContext(context, fact, count, 250, true); //context.AddToContext(context, fact, count, 250, true);
}
context.SaveChanges();
}
finally
{
if (context != null)
context.Dispose();
}
scope.Complete();
}
Console.WriteLine("batch {0} finished continuing", batch);
// continuing the batch
batch++;
skip = batchSize * (batch - 1);
doBatch = skip < total;
// AFTER THIS facts AND toProcess SHOULD BE RESET
// BUT IT LOOKS LIKE THEY ARE NOT OR AT LEAST SOMETHING
// IS GROWING IN MEMORY
}
Console.WriteLine("Processing is done {} recons processed", processed);
}
Die von Slauma zur Verfügung gestellte Methode zur Optimierung der Masseneinfügung mit Entity-Framework.
BTW, seien Sie sehr vorsichtig mit Anweisungen wie 'thisReconFacts.ForEach (f => facts.Add (f));' in einem 'Parallel.ForEach' Kontexten. 'Liste .Add (T)' ist nicht threadsicher. –
Ja, ich weiß, dass ich Thread-sichere Sammlungen verwenden sollte. Ich werde das als weitere Verbesserungen tun. Zuerst musste ich einen schnellen Weg finden, Tonnen von Daten mit EF einzufügen. –
Wer sagt, dass Sie threadsichere Sammlungen verwenden sollten? Was ist mit 'Liste facts = (aus Recon in toProcess.AsParallel() von Tatsache in ReconFactGenerator.Generate (Recon) wählen Sie die Tatsache) .ToList();'? –