使用 nHibernate 选择具有许多子集合的实体的性能不佳

Posted

技术标签:

【中文标题】使用 nHibernate 选择具有许多子集合的实体的性能不佳【英文标题】:Poor performance selecting entities with many child collections using nHibernate 【发布时间】:2013-07-24 12:51:30 【问题描述】:

当我尝试从数据库加载约 7700 条记录时,我正在使用的系统出现性能问题。与使用 SqlDataReader 的相同查询相比,NHB 的加载时间要长 10 倍。显然,使用 NHB 会产生一些性能开销,但 10 倍于 ADO.NET 的时间似乎过多。据我所知,NHB 是问题所在,特别是它如何处理挂在实体上的集合。

当我们尝试加载大量(ish)实体时,当实体本身有大量集合挂在它们上面时,我们的系统会遇到这个普遍问题。

我希望有人可以就如何改善这个问题提供一些建议。

查询很简单。

var employees = session.QueryOver<Employee>().List();

也没有发生 Select n+1 / 延迟提取问题。发出的单个 DB 语句实际上是 select * from employee

除非有人问,否则我不会发布实体的完整流畅映射,因为它相当大,但它相当标准。相关实体包括:

24 个字段 3 1-1 关系 86 1-* 关系

摘录:

HasMany(e => e.Roles)
.Access.CamelCaseField(Prefix.Underscore)
.Cascade.AllDeleteOrphan()
.Fetch.Select()
.Inverse()
.LazyLoad()
.KeyColumns.Add("[EmployeeId]")
.Cache.ReadWrite().IncludeAll();

以下是执行 1000 次的 5 次测试的平均时间。当我创建/处置 NHB 会话时,计时开始/停止,它不包括会话工厂的创建。

00:00:00.7080910 - 7677 records loaded/entity instantiation/manual map using ADO.NET
00:00:01.6055084 - 7677 entities - session - Fields Only
00:00:01.7866198 - 7677 entities - session - Fields + 1-1 Relationships
00:00:11.3384154 - 7677 entities - session - Fields + 1-1 Relationships + 1-* Relationships
00:00:10.9083002 - 7677 entities - stateless session - Fields + 1-1 Relationships + 1-* Relationships

需要明确的是,这不是数据库/网络/延迟获取问题。 IMO 的问题似乎在于 NHB 如何为实体补水。

编辑:尽管 fNHB 映射指定了缓存,但它在会话工厂中被禁用。我明确地将它们从映射文件中删除以进行验证,并且持续时间没有改变。

我还使用 ANTS 来分析代码,似乎大部分时间都花在了 TwoPhaseLoad.InitializeEntity 上。 (忽略其他 50% 的时间,下面没有显示。它的 nHibernates 会话工厂,这不是性能问题的原因)

环境: nHibernate 3.3.1.4000、fnHibernate 1.3.0.733、MS SQL Server 2008、c# 4.0、Windows 7-64。

【问题讨论】:

我的猜测是它与缓存有关,您使用的是什么缓存提供程序? ADO.NET 当然没有生成缓存键和检查缓存的开销。 86 个 1-* 关系是一个非常高的数字。 @JamieIde 不,它不是缓存。我应该这么说的。即使我们的 fNHB 映射包含缓存条目,会话工厂也是在禁用缓存的情况下创建的(通常,我们发现二级缓存会导致我们的应用程序出现严重的性能问题) @JamieIde 也许 86 1-* 关系对于 NHB 来说太多了,如果是这样的话,我无能为力。不过,我希望看到一些具体的东西,因为似乎很少有关于多少太多的信息。 它是线性增加的吗?那么如果只添加 1 个1-* 集合,那么时间是什么? 是的,Employee 可能是核心实体之一,但我认为@Firo 是在询问是否所有从 Employee 中萌芽的 集合 都是必要的。最好只映射应用程序实际需要的关系,并小心正确识别根聚合并相应地对关系建模。仅仅因为关系可以在 C# 方面被建模并不意味着它应该被建模。 【参考方案1】:

编写查询时,您必须将 .FetchMany(Func lambda) NHibernate 扩展包含到 IQueryable。

例如,如果您使用 Bars 查询一对多的 Foos:

class Foo 

    IList<Bar> Bars

而不是

session.Query&lt;Foo&gt;().ToList();(全部)

你应该写

session.Query&lt;Foo&gt;().FetchMany( src =&gt; src.Bars ).ToList();

这将告诉 nhibernate 获取连接而不是在每一行上获取选择。

【讨论】:

请阅读问题。这不是问题。我特别说“也没有发生 Select n+1 / 延迟获取问题”

以上是关于使用 nHibernate 选择具有许多子集合的实体的性能不佳的主要内容,如果未能解决你的问题,请参考以下文章

NHibernate:如何获取没有子集合的对象?

具有子集合的实体在 WCF 中导致问题:如果您使用 DataContractSerializer,请考虑使用 DataContractResolver

WPF TreeView HierarchicalDataTemplate - 绑定到具有多个子集合的对象

具有EF6子集合的父/子总是为空

Cloud Firestore 如何查找包含特定文档 ID 的子集合的所有文档?

读取带有子集合的文档的 Firestore 计费