提高 nHibernate 数据访问层的性能

Posted

技术标签:

【中文标题】提高 nHibernate 数据访问层的性能【英文标题】:Improving the performance of an nHibernate Data Access Layer 【发布时间】:2011-02-09 09:13:11 【问题描述】:

我正在努力提高现有 Asp.Net Web 应用程序的数据访问层的性能。风景如画。

    它是 Asp.Net 中基于 Web 的应用程序。 DataAccess 层使用 NHibernate 1.2 构建并公开为 WCF 服务。 Entity 类用 DataContract 标记。 不使用延迟加载,并且由于关系的急切获取,内存中没有大量的数据库对象加载。对数据库的命中数也很高。例如,我使用 NHProfiler 对应用程序进行了概要分析,大约有 50 多个 sql 调用使用主键加载其中一个 Entity 对象。 我也不能像现有的实时应用程序那样更改代码,根本没有 NUnit 测试用例。

请问我可以在这里得到一些建议吗?

编辑 1:我尝试过使用延迟加载,但问题是由于实体也用作 DataContract,它会在序列化期间触发延迟加载。 使用 DTO 对象是一种选择,但这是一个巨大的变化,因为没有实体是巨大的。如果没有测试用例,这将需要大量的手动测试工作。

编辑 2:该项目是很久以前写的,没有编写单元测试的灵活性。例如 实体本身包含 CRUD 操作并使用 NHibernate Session。

class SampleEntity : ICrudOperation

   //fields and properties

   public IList<SampleEntity> Update()
    

       //perform business logic (which can be huge and call the ICrudOperation of 
       //other entities

       ISession session = GetSessionFromSomewhere();
       session.Update(this); 

    


这只是更新的一个例子。并且有大约 400 个相互依赖的实体。有没有办法为此编写单元测试

【问题讨论】:

当架构的每个方面都出错时,就很难提高性能。 @Justice:这里出了什么问题?我们可以列出来吗?还可以做些什么来改进它而不会对现有正在运行的应用程序产生太大影响。 NHibernate 非常适合作为 DAL 本身并直接在您的应用程序中使用。 NHibernate 不适合构建将作为 WCF 服务公开的 DAL,然后将在您的应用程序中使用这些 DAL。您遇到的问题是使用 NHibernate 错误的自然结果。 你为什么不会编写单元测试?在您开始之前,您不会以任何有意义的方式成功迁移您的架构和工具库,IMO @Berryl:请参阅我的编辑 2。 【参考方案1】:

也许首先您应该切换到 NHibernate 的最新稳定版本(即 2.1.2,AFAIK),尽管我自己更喜欢使用 3.0 alpha 版本。较早的版本往往具有更高的稳定性和性能。而且我认为 NHibernate 的 API 向后兼容足以做到这一点。

所以,我为你推荐的解决方案是这样的:

如果您真的需要预先加载,用于返回对象以及完整对象图的查询数量仍然会很大,但别担心,有希望

NHibernate 是企业级 ORM,这意味着它可以很好地扩展。它可以针对各种场景采用各种缓存策略。

它有一个用于以下内容的内部缓存:

实体 收藏 查询 时间戳

它有两个层次:

一级缓存:缓存单个ISession 实例的内容。默认开启。 二级缓存:缓存来自同一个ISessionFactory的所有内容。默认关闭。

上面的描述对我来说是非常基本的,但这正是你所需要的。您可以在 nhibernate.info 和 Ayende's blog、several questions in *** 以及其他各种地方找到有关该主题的更多信息。

你需要做两件事:

确保您所有的ISessions 都来自同一个ISessionFactory 启用二级缓存(以上链接可以帮助您)

您将通过这种方法获得许多优势,但主要是因为您加载的所有实体都会被缓存,因此只需从数据库中请求它们一次。之后,它们将来自缓存。这大大减少了到数据库的往返次数。

【讨论】:

【参考方案2】:

建议一

    启用延迟加载 使用 风俗 NhibernateDataContractSerializerSurrogate 在代理被删除之前删除它们 通过 WCF (check out Allan Ritchie's reply here) 发送

这种组合将减少初始 SQL 连接的数量,并减小对象图的大小。

然而,这会导致下一个问题:

您的消费者/客户将无法访问所有数据。他们需要多次调用服务器来检索子对象/集合。

可以尝试预测将需要哪些子对象/集合(并急切地加载它们),但这可能是一个繁琐的练习。一个更简单的选择是检查空对象/集合(即在消费者处),并调用服务器以获取其他数据。

建议二

您的 SQL JOIN 速度慢吗?您可以尝试将 fetch=join 替换为 fetch=select,看看是否会加快速度。

建议三

确定花在 SQL 上的时间,与 通过网络发送数据 所用的时间。您可以考虑在通过网络发送之前压缩对象图。

建议 4

在将对象图返回给消费者之前,用您自己的自定义延迟加载代理交换任何 Nhibernate 延迟代理。您的自定义代理将通过 WCF/Web 服务延迟加载数据(而不是调用数据库)。我在富客户端上使用这种技术取得了一些成功。

理论上,这应该允许您的客户端代码保持原样。 There's a thread here 沿着这些思路。

希望对您有所帮助。

【讨论】:

我们已经在使用 fetch="select"。此外,我使用 Antz 和 NHProf 对应用程序进行了分析,这里的瓶颈是查询的数量。 建议 1 是我想要实现的目标,但似乎这也需要客户做出巨大的改变【参考方案3】:

除了其他人的建议之外,您是否考虑过使用 NHibernate Profiler 分析您的应用程序? (有 30 天试用期)

NHibernate Profiler 是一个实时的 允许开发的可视化调试器 团队获得宝贵的见解和 透视他们的使用 休眠。该产品是架构 来自许多***的输入 NHibernate 内的行业领导者 社区。警报以 简洁的代码审查方式指示 您的滥用模式 应用。简化您的 努力纠正误用,我们 提供有问题代码的链接 触发警报的部分。

【讨论】:

【参考方案4】:

看来这里的架构可以改进。

主要问题似乎是正在读取大量数据。是否需要读取所有这些数据?例如,如果实体 A 有一个正在急切加载的子元素 B 的列表,但只有实体 A 的字段显示在页面上,则只需要读取实体 A。如果页面上显示了所有内容,请考虑重新设计页面,以便您必须导航到其他页面才能查看实体 B 数据。

假设您只显示来自实体 A 的数据,或者可以重新设计网站以执行此操作,那么第一件事就是打开子实体的延迟加载,以便您只需要阅读它们如果你真的需要数据。其次,如果您继续返回实体本身,开启延迟加载将不起作用,因为当序列化程序序列化您的数据时,仍然会读取子实体。您将需要引入一些数据传输对象 (DTO) 以通过网络传递数据。这些将类似于您的实体,但只会包含您实际想要在网页上使用的数据的字段。然后,您需要将您的实体转换为 DTO,这意味着由于您不会访问不需要的子实体列表,并且配置了延迟加载,因此不会读取该数据。

值得研究升级到最新版本的 NHibernate,尽管没有单元测试这可能会很可怕,但绝对值得。

引入二级缓存可能会产生很小的影响,因为当您在分布式环境中获得大量命中时,这确实开始产生影响。你有更根本的问题要先解决。

【讨论】:

我尝试过使用最新版本,但它破坏了很多东西。同样对于使用 DTO,我需要先配置延迟加载,因为即使我基于主键查询也会加载关系。 新版 NHibernate 会给这里带来哪些好处? 这里的主要问题 (IMO) 是急切地加载所有内容,因此如果不使用延迟加载,您将很难获得任何性能提升,因为所有内容都将继续同时加载。如果不引入 DTO,您将不会获得延迟加载的好处。很简单,没有灵丹妙药可以解决这个问题。更高版本的 NHibernate 有性能改进,因此升级 NHibernate 可能会有所帮助。【参考方案5】:

在 NHibernate 中配置缓存策略....

【讨论】:

你的意思是二级缓存吗?它与 NHibernate 1.2 一起工作可靠吗?

以上是关于提高 nHibernate 数据访问层的性能的主要内容,如果未能解决你的问题,请参考以下文章

Nhibernate学习教程-- 第一个NHibernate程序

提高反射效率

实体框架和 NHibernate - 缓存仍然是服务层的责任吗?

想请问一下Adp.net与Ado.net Entity Framework、NHibernate性能对比

Buffer Cache

linux TLB表