NHibernate SQL查询速度慢

时间:2011-07-28 13:33:02

标签: .net sql nhibernate

我正在使用LINQ to NH来获取应用启动时的大量数据。我特意添加ToList()以强制立即执行查询:

Group group = GetGroup();
Log.Info("started");
var list = Session.Linq<Data>()
    .Where(p => p.Group.Id == group.Id)
    .OrderByDescending(p => p.Stamp.Counter) /* Stamp is composite mapping */
    .Select(p => new
    {
        Counter = p.Stamp.Counter,
        Status = p.Status,
    })
    .Take(4000)
    .ToList();
Log.Info("done");

检查NHibernate.SQL logger的DEBUG日志会按预期提供以下SQL(当我开始监视时,SQL Profiler中会弹出相同的查询):

SELECT top 4000 this_.Counter as y0_, this_.Status as y1_
FROM [Data] this_ 
LEFT OUTER JOIN [Group] group1_ ON this_.Group_id=group1_.Id
WHERE group1_.Id = @p0 
ORDER BY this_.Counter desc; @p0 = 1

问题是,从我的应用程序调用时,此查询需要2分钟才能完成,而在SSMS中执行则需要0.5秒!实际上,当应用程序等待查询完成时,我可以在SSMS中执行它并立即获得结果。

您认为这种差异来自哪里?

4 个答案:

答案 0 :(得分:4)

由于关于您的应用程序的信息不多,我只能猜测。

NH的性能问题通常是由刷新缓存引起的。在每次查询之前刷新缓存。当会话中有很多实体时,可能需要花费很多时间。请尝试以下方法:

Log.Info("Flushing");
Session.Flush();
Session.FlushMode = FlushMode.Never;

Log.Info("Query");
var list = Session.Linq<Data>()
    //...
Log.Info("Done");
// for production code, this belongs into a finally block
Session.FlushMode = FlushMode.Auto; 

如果实际上 是一个刷新问题,则需要在事务中的某些点上手动刷新。关闭自动冲洗时要小心。它可能会导致丑陋的副作用。这是非常具体的交易,我不能说你如何以正确的方式实现它。您也可以使用StatelessSession,但对我来说它从未奏效(它有一些限制)。您也可以清除会话,这也要求您确切知道自己在做什么。

如果没有冲洗问题,则很难跟踪。使用Profiler查看它是否实际占用SQL Server查询中的时间。它甚至可能是SQL服务器上的缓存问题。在这种情况下,第一次执行查询时需要几分钟,但第二次只需几秒钟。创建适当的索引可能有所帮在这里,我停止猜测...

答案 1 :(得分:3)

我的假设是有一些拦截器会减慢对象的实现或加载(即N + 1问题)。

我做了一些测试,甚至30 000个对象也无法减慢获取对象列表的速度(从本地机器500ms获取30000个对象的列表,从远程数据库 - 4秒)。

答案 2 :(得分:2)

有几个可能的原因:

  • 你将至少4000个对象加载到内存中,给它们加以保湿,NHibernate也必须控制Session和第一级缓存中每个加载的对象
  • 我没有看到你的映射,但很可能在某些时候会出现某种急切的加载,也会发送其他查询和加载其他对象等等。

这些来自我的头脑,还有我更多。另外检查NHibernate的日志级别是否未设置为DEBUG,它非常详细,可以消耗大量资源。

答案 3 :(得分:2)

今天一个项目的一个不错的观点:

我搜索了大约一周的原因,因为我的nHibernate查询(使用期货加载某些集合的多标准)需要11秒(在MSSQL探查器中持续时间),如果我在SSMS中执行完全相同的组合查询,则大约需要2秒。< / p>

解决方案是:我激活了一些日志来运行Ayendes profiler。 NHProf dll丢失了,但是:nHibernate中的一些GetRows方法在水合期间触发日志调用。差异是:9秒!

我刚刚注释掉了log4net配置调用,延迟几乎消失了。

我有大约14.000个实例加上60.000个HasMany集合条目。水合现在需要0.6秒,因为SQL语句需要2秒(这是另一个优化故事)。

并且:我认为水合持续时间和查询执行持续时间一起显示在SQL事件探查器“duration”列中。

2周前的另一个故事是:SQL分析器中的执行计划与在SSMS中执行查询时提供的执行计划不同。原因是,我在nHibernate中使用了OLEDB提供程序。我切换到ADO连接,执行计划是一样的。我在查看MS SQL分析器中的一些“协议版本”列时发现了这一点。

除了n + 1之外,性能陷阱有很多原因:)

祝福! 迈克尔