primer - nHibernate el problema de rendimiento al cargar grandes colecciones
java hql (3)
Usaría una sesión sin estado y una optimización por lotes.
La sesión hará un seguimiento de todos los objetos cargados, y si cargamos una gran cantidad de datos, eventualmente explotará con una excepción de falta de memoria. Afortunadamente, NHibernate tiene una solución preparada para esto, la sesión sin estado. El código ahora se ve así:
using (IStatelessSession s = sessionFactory.OpenStatelessSession())
{
var books = new ActionableList<Book>(book => Console.WriteLine(book.Name));
s.CreateQuery("from Book")
.List(books);
}
La sesión sin estado, a diferencia de la sesión normal de NHibernate, no hace un seguimiento de los objetos cargados, por lo que el código aquí y el código del lector de datos son esencialmente lo mismo.
Para la optimización de lotes y más: trucos de rendimiento NHibernate .
(solo para dejar en claro: mi aplicación no se trata realmente de empleados y departamentos. Simplemente uso estos términos por ejemplo).
Cada departamento tiene una colección de empleados, que está cargada de manera perezosa. Cada vez que agrego un nuevo empleado, quiero asegurarme de que no exista en la colección, así que cargo la colección en la memoria y la controlo.
El problema es que en el entorno de producción, tengo algunos departamentos con más de 10.000 empleados.
Descubrí que buscar la colección y luego salvar al nuevo empleado lleva MUCHO tiempo.
He hecho un pequeño experimento, en el que copié exactamente la misma declaración select generada por nH a ADO.Net SQLDataAdapter. Aquí están los resultados:
***16:04:50:437*** DEBUG NHibernate.SQL - SELECT ... FROM dbo.[Employee] emp0_ left outer join dbo.[Department] department1_ on emp0_.Department_id=department1_.Id left outer join dbo.[TableC] TableC2_ on department1_.TableC_id=TableC2_.Id WHERE emp0_.SomeField_id=@p0;@p0 = 2
***16:05:00:250*** DEBUG NHibernate.SQL - SELECT ... FROM dbo.TableD codeshared0_ left outer join dbo.[Department] department1_ on codeshared0_.Department_id=department1_.Id left outer join dbo.[TableC] TableC2_ on department1_.TableC_id=TableC2_.Id WHERE codeshared0_.Employee_id in (select emp0_.Id FROM dbo.[Employee] emp0_ left outer join dbo.[Department] department1_ on emp0_.Department_id=department1_.Id left outer join dbo.[TableC] TableC2_ on department1_.TableC_id=TableC2_.Id WHERE emp0_.SomeField_id=@p0);@p0 = 2
16:05:04:984 DEBUG NHibernate.SQL - Reading high value:select next_hi from dbo._uniqueKey with (updlock, rowlock)
16:05:05:078 DEBUG NHibernate.SQL - Updating high value:update dbo._uniqueKey set next_hi = @p0 where next_hi = @p1;@p0 = 10686, @p1 = 10685
***16:05:05:328*** DEBUG MyApp.Managers - commiting
16:05:12:000 DEBUG NHibernate.SQL - INSERT INTO dbo.[Employee] (...) VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9);@p0 = 23/04/2011 04:04:49, @p1 = 23/04/2011 03:34:49, @p2 = 23/04/2011 04:04:49, @p3 = 23/04/2011 03:34:49, @p4 = '''', @p5 = False, @p6 = 433, @p7 = NULL, @p8 = 2, @p9 = 10685
16:05:12:140 DEBUG NHibernate.SQL - UPDATE dbo.[Employee] SET Department_id = @p0 WHERE Id = @p1;@p0 = 2, @p1 = 10685
16:05:12:343 DEBUG MyApp.Managers - success
16:05:12:359 DEBUG MyApp.Tests - ------------------------------------------------------------
16:05:12:359 DEBUG MyApp.Tests - Finished nHib stuff- now switching to ADO
16:05:12:359 DEBUG MyApp.Tests - starting SQL: SELECT ... FROM dbo.[Employee] emp0_ left outer join dbo.[Department] department1_ on emp0_.Department_id=department1_.Id left outer join dbo.[TableC] TableC2_ on department1_.TableC_id=TableC2_.Id WHERE emp0_.SomeField_id=2
16:05:14:750 DEBUG MyApp.Tests - total rows received: 10036
16:05:14:750 DEBUG MyApp.Tests - SQL: SELECT ... FROM dbo.TableD codeshared0_ left outer join dbo.[Department] department1_ on codeshared0_.Department_id=department1_.Id left outer join dbo.[TableC] TableC2_ on department1_.TableC_id=TableC2_.Id WHERE codeshared0_.Employee_id in (select emp0_.Id FROM dbo.[Employee] emp0_ left outer join dbo.[Department] department1_ on emp0_.Department_id=department1_.Id left outer join dbo.[TableC] TableC2_ on department1_.TableC_id=TableC2_.Id WHERE emp0_.SomeField_id=2)
16:05:15:250 DEBUG MyApp.Tests - total rows received: 2421
como puede ver, la búsqueda lleva ~ 15 segundos con nH, en comparación con ~ 2 segundos con ADO.Net.
Al investigar un poco, sé que nH probablemente no esté destinado a almacenar tantos elementos en sesión. ¿Puede pensar en alguna otra razón posible para este problema o en otra sugerencia que no sea filtrar a los empleados en el nivel de la base de datos?
Gracias
- EDITAR -
Siguiendo las sugerencias a continuación, he intentado utilizar Reflection Optimizer (no hizo diferencia), e IStatelessSession para cargar mi colección (arroja una excepción, las colecciones no pueden ser recuperadas por una sesión sin estado).
Creo que mi código en la clase de departamento tendrá que cambiar desde el limpio:
if (this.Employees.Contains(emp))
{
...
}
a esta versión ''más sucia'':
var employeesRepository = IOCContainer.Get<IEmployeesRepository>();
if (employeesRepository.EmployeeExists(this,emp))
{
...
}
alguien tiene una mejor sugerencia?
No hay ninguna razón para que cargue todos los empoweres a la memoria. debe escribir una consulta usando HQL / Critiria API / Linq a NHibernate para verificar si el empleado ya existe en la base de datos. por ejemplo:
var existingEmpoyee = session.Query<Employee>()
.Where(e => e.Equals(newEmployee))
.FirstOrDefault();
if(existingEmployee != null)
// Insert new employee to DB
hmmmm puede ser demasiado, y de hecho, espero que un ISet "perezoso = extra" se comporte así, pero puede escribir su propio ISet "extra perezoso". si no encontró una colección extra diferida, es una colección que, por ejemplo, cuando solicita su recuento, no recupera todo pero emite una consulta de recuento. su ISet extra perezoso podría emitir una consulta existente cada vez que intente agregar algo.
Si implementa esto, su código estaría limpio y podría enviar el código a nhibernate core.
sin embargo, debes pensar en agregar rango y ten cuidado de no enviar N consultas
buena suerte.