我有以下代码行:
log4net.LogManager.GetLogger("m").Debug(DateTime.Now.ToString("hh:mm:ss.fff") + " Check-1");
Setting setting = session.CreateQuery("from Setting s").UniqueResult< Setting>();
log4net.LogManager.GetLogger("m").Debug(DateTime.Now.ToString("hh:mm:ss.fff") + " Check-2");
上面的代码执行时间不到毫秒(Check-1 和 Check-2 中的时间相同,并且以毫秒为单位测量时间,并且代码中未显示所使用的标准)。
但在这种情况下:
IList< Ticket> tickets = session.CreateQuery("from Ticket").List< Ticket>();
foreach(Ticket t in tickets)
{
t.Dosomething = 5;
log4net.LogManager.GetLogger("m").Debug(DateTime.Now.ToString("hh:mm:ss.fff") + " Check-1"); Setting setting = session.CreateQuery("from Setting s").UniqueResult< Setting>(); log4net.LogManager.GetLogger("m").Debug(DateTime.Now.ToString("hh:mm:ss.fff") + " Check-2");
}
上面的代码是处理大量数据的庞大处理代码的简化版本,我需要在 foreach 循环中进行另一个查询。 forech 循环中的查询在 500 毫秒内执行。票证集合包含 15000 行。
我已经重构了代码(foreach中没有查询),但我感兴趣的是为什么相同的查询如果单独执行则不需要时间执行,但如果在先加载大量实体之后执行,它会变得如此慢?
但是在上面的相同场景中,如果我对第二个查询使用不同的 session ,它会立即执行。
当我需要在大量实体的 foreach 循环中执行另一个查询时,有什么建议如何处理这种情况?
最佳答案
NHibernate 默认情况下跟踪 session 中加载的对象(一级缓存)。当进行查询时,NHibernate 检查某些加载的对象是否发生了可能影响查询结果的更改 - 此类更改必须刷新到数据库,以便查询可以返回正确的结果。加载的对象越多,此过程花费的时间就越长。
NHibernate 和 ISession 针对加载对象数量相对较少进行了优化,这不是问题。
以下是提高批处理场景性能的一些技巧:
尝试将工作划分为更小的部分,这些部分可以在单独的事务和 session 中独立运行。
如果您已将工作分成更小的部分,则可以在战略点(在每个部分之后)使用
session.Flush()
和session.Clear()
独立部分)以刷新更改并将跟踪对象的数量保持在合理的水平。通过这种策略,所有部分都可以在同一个事务中运行。查看
session.FlushMode
以禁用查询时自动刷新。将FlushMode.Commit
或Never
与显式调用Flush()
结合使用。但查询结果可能无法反射(reflect)同一工作单元中先前执行的更改。
关于java - 具有大量实体的 hibernate 性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15228436/