问题描述
|
(只需说明一下:我的应用与员工和部门无关。我只是出于举例目的使用这些术语)。
每个部门都有一个员工集合,这些集合是延迟加载的。每当我添加新员工时,我都想确保它在集合中尚不存在,因此我将集合加载到内存中并对其进行检查。
问题是-在生产环境中,我有些部门的员工人数超过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个查询
祝好运。