【发布时间】:2026-01-27 11:35:02
【问题描述】:
我有一个使用 NHibernate 作为其 ORM 的应用程序,有时它会因为它访问数据的方式而遇到性能问题。可以做哪些事情来提高 NHibernate 的性能? (请限制每个答案推荐一个)
【问题讨论】:
标签: .net performance nhibernate orm data-access-layer
我有一个使用 NHibernate 作为其 ORM 的应用程序,有时它会因为它访问数据的方式而遇到性能问题。可以做哪些事情来提高 NHibernate 的性能? (请限制每个答案推荐一个)
【问题讨论】:
标签: .net performance nhibernate orm data-access-layer
如果您还没有(适当地)使用延迟加载,请开始。在不需要时获取集合是浪费一切。
Chapter Improving performance 描述了这种方法和其他提高性能的方法。
【讨论】:
分析是第一步——即使是简单的定时单元测试——找出可以在哪里获得最大收益
对于集合,请考虑设置批处理大小以减少发出的 select 语句的数量 - 有关详细信息,请参阅部分 Improving performance
【讨论】:
lotsoffreetime 说了什么。
阅读文档的第 19 章,“提高性能”。
NHibernate:http://nhibernate.info/doc/nhibernate-reference/performance.html
休眠:http://docs.jboss.org/hibernate/core/3.3/reference/en/html/performance.html
使用 SQL Profiler(或您所使用的数据库的等效工具)查找长时间运行的查询。使用适当的索引优化这些查询。
对于几乎在应用程序的每个页面上使用的数据库调用,使用 CreateMultiQuery 从单个数据库查询返回多个结果集。
当然还有缓存。页面/控件的 OutputCache 指令。用于数据的 NHibernate 缓存。
【讨论】:
仅“每个答案一个推荐”?然后我会选择这个:
避免连接重复(AKA 笛卡尔积),因为连接沿两个或多个平行多对多关联;请改用 Exists-subqueries、MultiQueries 或 FetchMode "subselect"。
【讨论】:
没有推荐,但可以帮助您的工具:NH Prof (http://nhprof.com/) 似乎很有希望,它可以评估您对 ORM 框架的使用。这可能是您调整 NHibernate 的一个很好的起点。
【讨论】:
通过识别何时从延迟加载切换到针对执行缓慢的查询的急切获取来避免和/或最小化Select N + 1 problem。
【讨论】:
NHibernate 的 SessionFactory 是一项昂贵的操作,因此一个好的策略是创建一个 Singleton 以确保内存中只有一个 SessionFactory 实例:
public class NHibernateSessionManager
{
private readonly ISessionFactory _sessionFactory;
public static readonly NHibernateSessionManager Instance = new NHibernateSessionManager();
private NHibernateSessionManager()
{
if (_sessionFactory == null)
{
System.Diagnostics.Debug.WriteLine("Factory was null - creating one");
_sessionFactory = (new Configuration().Configure().BuildSessionFactory());
}
}
public ISession GetSession()
{
return _sessionFactory.OpenSession();
}
public void Initialize()
{
ISession disposeMe = Instance.GetSession();
}
}
然后在你的 Global.Asax Application_Startup 中,你可以初始化它:
protected void Application_Start()
{
NHibernateSessionManager.Instance.Initialize();
}
【讨论】:
缓存、缓存、缓存——您是否正确使用了一级缓存[过早关闭会话,或者使用 StatelessSession 绕过一级缓存]?您是否需要为不经常更改的值设置一个简单的二级缓存?您能否缓存查询结果集以加快不经常更改的查询?
[还有配置——你可以将项目设置为不可变的吗?您能否重组查询以仅返回您需要的信息并将它们转换为原始实体?蝙蝠侠能在谜语人到达大坝之前阻止他吗? ...哦,对不起,走神了。]
【讨论】:
您在使用 NHibernate 时可能遇到的第一个也是最严重的性能问题是,如果您要为您创建的每个会话创建一个新的会话工厂。每次应用程序执行只应创建一个会话工厂实例,并且所有会话都应由该工厂创建。
按照这些思路,只要有意义,您就应该继续使用相同的会话。这将因应用程序而异,但对于大多数 Web 应用程序,建议每个请求一个会话。如果您经常丢弃会话,则不会获得缓存的好处。智能地使用会话缓存可以将具有线性(或更差)查询数的例程更改为常数,而无需做太多工作。
同样重要的是,您要确保延迟加载对象引用。如果不是,即使是最简单的查询也可以加载整个对象图。不这样做只有某些原因,但最好先从延迟加载开始,然后根据需要切换回来。
这给我们带来了渴望获取,与延迟加载相反。在遍历对象层次结构或循环遍历集合时,很容易忘记您正在执行多少查询,并且最终会得到指数级的查询。可以使用 FETCH JOIN 在每个查询的基础上完成 Eager fetching。在极少数情况下,例如如果有一对特定的表您总是获取连接,请考虑关闭该关系的延迟加载。
与往常一样,SQL Profiler 是查找运行缓慢或重复执行的查询的好方法。在我的上一份工作中,我们有一个开发功能,可以计算每个页面请求的查询。一个例程的大量查询是您的例程不能很好地与 NHibernate 一起工作的最明显的指标。如果每个例程或请求的查询数量看起来不错,那么您可能需要进行数据库调优;确保您有足够的内存来在缓存中存储执行计划和数据,正确索引您的数据等。
我们遇到的一个棘手的小问题是 SetParameterList()。该函数允许您轻松地将参数列表传递给查询。 NHibernate 通过为传入的每个项目创建一个参数来实现这一点。这导致每个参数数量的不同查询计划。我们的执行计划几乎总是从缓存中释放出来。此外,许多参数会显着减慢查询速度。我们对 NHibernate 进行了自定义修改,以将项目作为分隔列表发送到单个参数中。该列表在 SQL Server 中由表值函数分隔,我们的 hack 自动插入到查询的 IN 子句中。根据您的应用,可能还有其他类似的地雷。 SQL Profiler 是找到它们的最佳方式。
【讨论】:
我只能将答案限制在一个选项上?在这种情况下,我会选择您实现 NHibernate 的二级缓存机制。
这样,您可以为映射文件中的每个对象定义缓存策略。二级缓存会将已检索到的对象保存在内存中,因此不会再次往返数据库。这是一个巨大的性能提升。
您的目标是定义您的应用程序不断访问的对象。其中包括常规设置等。
关于 nhibernate 二级缓存以及如何实现它,有很多信息可以找到。
祝你好运:)
【讨论】:
NHibernate 开箱即用地生成相当快的 SQL。 我已经使用它一年了,还没有用它编写裸 SQL。 我所有的性能问题都来自Normalization 和缺少索引。
最简单的解决方法是检查查询的执行计划并创建适当的索引,尤其是在外键列上。如果您使用的是 Microsoft SQL Server,“Database Engine Tuning Advisor”会对此有很大帮助。
【讨论】:
没有关于您所看到的性能问题种类的任何细节,我只能提供一个概括:根据我的经验,大多数数据库查询性能问题是由于缺乏适当的索引而引起的。因此,我对第一个操作的建议是检查您的非索引查询的查询计划。
【讨论】: