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