n加载大型集合时的休眠性能问题

问题描述

| (只需说明一下:我的应用与员工和部门无关。我只是出于举例目的使用这些术语)。 每个部门都有一个员工集合,这些集合是延迟加载的。每当我添加新员工时,我都想确保它在集合中尚不存在,因此我将集合加载到内存中并对其进行检查。 问题是-在生产环境中,我有些部门的员工人数超过10,000。 我发现获取集合然后保存新员工需要很多时间。 我做了一些实验,在其中我将nH生成的完全相同的select语句复制到ADO.Net sqlDataAdapter。结果如下:
***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
如您所见,使用nH进行读取大约需要15秒,而使用ADO.Net则需要大约2秒。 通过一些研究,我知道nH可能并不意味着用于存储会话中的许多项。您能否想到此问题的任何其他可能原因,或者除了在数据库级别过滤雇员之外的其他建议? 谢谢 - 编辑 - 按照以下建议,我尝试使用Reflection Optimizer(无差异)和IStatelessSession来加载我的集合(引发异常,无状态会话无法获取异常集合)。 我认为我在Department类中的代码必须从干净的地方更改为:
if (this.Employees.Contains(emp))
{
  ...
}  
此\'dirtier \'版本:
var employeesRepository = IOCContainer.Get<IEmployeesRepository>();  
if (employeesRepository.EmployeeExists(this,emp))
{
  ...
}  
有人有更好的建议吗?     

解决方法

您没有理由将所有员工加载到内存中。你应该写一个查询 使用NHibernate的HQL / Critiria API / Linq来检查数据库中是否已存在该员工。 例如:
var existingEmpoyee = session.Query<Employee>()
                             .Where(e => e.Equals(newEmployee))
                             .FirstOrDefault();
if(existingEmployee != null)
   // Insert new employee to DB
    ,我将使用StatelessSession和批处理优化。   该会话将跟踪所有   加载的对象,如果我们加载很多   数据,最终将爆炸   内存不足异常。   幸运的是,NHibernate已经准备好了   解决方案,无状态   会议。现在的代码如下所示:
using (IStatelessSession s = sessionFactory.OpenStatelessSession())
{
    var books = new ActionableList<Book>(book => Console.WriteLine(book.Name));
    s.CreateQuery(\"from Book\")
        .List(books);

}
  无状态会话与   正常的NHibernate会话,不是   跟踪加载的对象,因此   这里的代码和数据读取器代码是   本质上是一样的。 对于批处理优化及其他:NHibernate性能技巧。     ,嗯。可能太多了,实际上-我希望\“ lazy = extra \” ISet的行为像这样,但是您可以编写自己的\“ lazy = extra \” ISet。 如果您没有遇到额外的惰性集合-例如,当您询问其计数时,该集合不会获取所有内容,而是发出计数查询。 每当您尝试添加某些内容时,您的额外惰性ISet都可能会发出一个存在查询。 如果实现了这一点,您的代码将很干净,您可以将代码提交给nhibernate core。 但是,您应该考虑添加范围,并注意不要发出N个查询 祝好运。