2010-04-28 9 views
15

Estoy procesando 1 millón de registros en mi aplicación, que recupero de una base de datos MySQL. Para hacerlo, estoy usando Linq para obtener los registros y uso .Skip() y .Take() para procesar 250 registros a la vez. Para cada registro recuperado, necesito crear de 0 a 4 elementos, que luego agrego a la base de datos. Entonces, la cantidad promedio de ítems totales que debe crearse es de alrededor de 2 millones.Sin memoria al crear muchos objetos C#

IQueryable<Object> objectCollection = dataContext.Repository<Object>(); 
int amountToSkip = 0; 
IList<Object> objects = objectCollection.Skip(amountToSkip).Take(250).ToList(); 
while (objects.Count != 0) 
     { 
      using (dataContext = new LinqToSqlContext(new DataContext())) 
      { 
       foreach (Object objectRecord in objects) 
       { 
        // Create 0 - 4 Random Items 
        for (int i = 0; i < Random.Next(0, 4); i++) 
        { 
         Item item = new Item(); 
         item.Id = Guid.NewGuid(); 
         item.Object = objectRecord.Id; 
         item.Created = DateTime.Now; 
         item.Changed = DateTime.Now; 
         dataContext.InsertOnSubmit(item); 
        } 
       } 
       dataContext.SubmitChanges(); 
      } 
      amountToSkip += 250; 
      objects = objectCollection.Skip(amountToSkip).Take(250).ToList(); 
     } 

Ahora el problema surge al crear los elementos. Cuando se ejecuta la aplicación (y ni siquiera usando dataContext), la memoria aumenta constantemente. Es como si los artículos nunca fueran eliminados. ¿Alguien se da cuenta de lo que estoy haciendo mal?

¡Gracias de antemano!

+0

¿Cómo inicializas tu objectCollection? –

+0

IQueryable objectCollection = dataContext.Repository (); – Bas

+0

¿Por qué toma lotes de 250 en lugar de simplemente iterar sobre objectCollection? – Jens

Respuesta

7

Ok he acabo de discutir esta situación con un colega mío y nos ¡He llegado a la siguiente solución que funciona!

int amountToSkip = 0; 
var finished = false; 
while (!finished) 
{ 
     using (var dataContext = new LinqToSqlContext(new DataContext())) 
     { 
      var objects = dataContext.Repository<Object>().Skip(amountToSkip).Take(250).ToList(); 
      if (objects.Count == 0) 
       finished = true; 
      else 
      { 
       foreach (Object object in objects) 
       { 
        // Create 0 - 4 Random Items 
        for (int i = 0; i < Random.Next(0, 4); i++) 
        { 
         Item item = new Item(); 
         item.Id = Guid.NewGuid(); 
         item.Object = object.Id; 
         item.Created = DateTime.Now; 
         item.Changed = DateTime.Now; 
         dataContext.InsertOnSubmit(item); 
        } 
       } 
       dataContext.SubmitChanges(); 
      } 
      // Cumulate amountToSkip with processAmount so we don't go over the same Items again 
      amountToSkip += processAmount; 
     } 
} 

Con esta aplicación disponemos del salto() y Take() cada vez que la memoria caché y así no perder memoria!

2

Mi mejor estimación aquí sería la IQueryable para causar la pérdida de memoria. Tal vez no hay una implementación apropiada para MySQL de los métodos Take/Skip y está haciendo el paginado en la memoria? Han sucedido cosas más extrañas, pero su ciclo se ve bien. Todas las referencias deben estar fuera del alcance y obtener basura recolectada ..

0

Bueno, sí.

Así que al final de ese ciclo intentará tener 2 millones de elementos en su lista, ¿no? Me parece que la respuesta es trivial: almacene menos elementos u obtenga más memoria.

- Editar:

Es posible que he leído mal, probablemente necesitaría para compilar y probarlo, pero no puedo hacer eso ahora. Dejaré esto aquí, pero podría estar equivocado, no lo he revisado con cuidado para ser definitivo, sin embargo, la respuesta puede ser útil o no. (A juzgar por el downvote, supongo que no: P)

+0

No tiene una base de datos con 2 millones de entradas, las toma 250 a la vez y luego agrega 4 nuevas sub-entradas a la base de datos. En la memoria no hay ninguna lista de ningún tipo .. Lea la pregunta .. – Tigraine

+0

@Tigraine Creo que es el problema lo que lo está jodiendo. Esa es mi suposición, de todos modos. –

+0

El caché, en la lista objectCollection, es el cuello de botella. No se eliminó automáticamente – Bas

0

Ha intentado declarar el artículo fuera del bucle como este:

IQueryable<Object> objectCollection = dataContext.Repository<Object>(); 
int amountToSkip = 0; 
IList<Object> objects = objectCollection.Skip(amountToSkip).Take(250).ToList(); 
Item item = null; 
while (objects.Count != 0) 
     { 
      using (dataContext = new LinqToSqlContext(new DataContext())) 
      { 
       foreach (Object objectRecord in objects) 
       { 
        // Create 0 - 4 Random Items 
        for (int i = 0; i < Random.Next(0, 4); i++) 
        { 
         item = new Item(); 
         item.Id = Guid.NewGuid(); 
         item.Object = objectRecord.Id; 
         item.Created = DateTime.Now; 
         item.Changed = DateTime.Now; 
         dataContext.InsertOnSubmit(item); 
        } 
       } 
       dataContext.SubmitChanges(); 
      } 
      amountToSkip += 250; 
      objects = objectCollection.Skip(amountToSkip).Take(250).ToList(); 
     } 
+0

Lo intenté, no funcionó>. Bas

5

Ahhh, el viejo InsertOnSubmit pérdida de memoria. Lo he encontrado y golpeé mi cabeza contra la pared muchas veces cuando intento cargar datos de archivos CVS grandes usando LINQ to SQL. El problema es que incluso después de llamar al SubmitChanges, el DataContext continúa rastreando todos los objetos que se han agregado utilizando InsertOnSubmit. La solución es SubmitChanges después de una cierta cantidad de objetos, luego cree un nuevo DataContext para el siguiente lote. Cuando el antiguo DataContext es basura, también lo harán todos los objetos insertados que son rastreados por él (y que ya no necesita).

"¡Pero espera!" usted dice, "¡Crear y deshacerse de muchos DataContext tendrá una sobrecarga enorme!". Bueno, no si usted crea una conexión de base de datos única y la pasa a cada constructor DataContext. De esta forma, se mantiene una conexión única a la base de datos, y el objeto DataContext es un objeto ligero que representa una unidad de trabajo pequeña y debe descartarse después de completarse (en su ejemplo, presentar una cierta cantidad de registros).

+0

Ehh He dicho en mi pregunta que incluso cuando estoy NO usando el DataContext estoy obteniendo esta fuga, por lo que no está obligado a InsertOnSubmit o SubmitChanges (ya lo he probado) Y es una buena práctica usar DataContext en un bloque en uso. Los DataContextes son lightweigth y están destinados a ser recreados mucho (ver: http://stackoverflow.com/questions/123057/how-do-i-avoid-a-memory-leak-with-linq-to-sql). Ya he intentado usar 1 DataContext para hacer todas las transacciones, eso fue incluso peor. – Bas

+0

Está repitiendo lo que dije: use un nuevo DataContext para cada unidad de trabajo pequeña (dentro de una declaración 'using', por supuesto). ¿Y cómo exactamente probaste tu ejemplo sin un 'DataContext'? ¿De dónde sacaste la colección 'objects'? –

+0

Lo siento, quise decir sin las llamadas InsertOnSubmit y SubmitChanges;] My bad. Al principio también pensé que InsertOnSubmit y SubmitChanges eran el problema, después de arreglar esto y hacer una segunda corrida, aún recibí la filtración. La fuga se debe a Skip and Take que divide todos los elementos recuperados y nunca los descarta automáticamente mientras se ejecuta. Así que finalmente tuve 2 millones de elementos en una lista chateada. – Bas

Cuestiones relacionadas