如何“预热”Entity Framework?它何时会变“冷”?

119

不,我的第二个问题的答案不是冬天。

前言:

最近我一直在研究实体框架,并且有一个问题一直困扰着我,那就是当查询没有预热时(所谓的冷查询)它的性能。我阅读了 Entity Framework 5.0 的 性能考虑 文章。作者介绍了温热和冷查询的概念以及它们的区别,而我自己在使用时也注意到了这点。这里可能值得一提的是我只有六个月的经验。

现在我知道要想更好地理解该框架的性能方面,需要进一步研究的主题。不幸的是,互联网上的大部分信息已经过时或充斥着主观性,因此我无法在 "温热" vs. "冷" 查询的主题上找到任何其他信息。

到目前为止,我已经注意到的基本情况是,每当我必须重新编译或运行回收站时,我的初始查询变得非常缓慢。随后的数据读取很快(主观),正如预期的那样。

我们将迁移到 Windows Server 2012、IIS8 和 SQL Server 2012,作为一名初级员工,我实际上赢得了在其他人之前测试它们的机会。我很高兴他们引入了一个预热模块,可以让我的应用程序准备好第一个请求。然而,我不确定如何继续预热我的实体框架。

我已经知道值得做的事情:

  • 根据建议提前生成我的视图。
  • 最终将我的模型移动到单独的程序集中。

我正在考虑采取常识方法,这可能是错误的方法

  • 在应用程序启动时执行虚拟数据读取,以便预热并生成和验证模型。

问题:

  • 在任何时候保持实体框架的高可用性的最佳方法是什么?
  • 在哪些情况下,实体框架会再次“变冷”?(重新编译、回收、IIS重启等)

  • 我已经提到了视图生成@Pawel,层次结构并不复杂,甚至一点也不。但问题也很重要。根据你的说法,我将研究应用程序域何时被卸载。然而,这仍然无法解决另一个问题,即在情况下预热Entity Framework,就像你所说的,如果应用程序域被卸载。此时,似乎应用程序域被卸载的频率超过了应有的程度,我不确定原因是什么,回收只在晚上进行,空闲设置为0。 - user1372494
    4
    为什么您认为进行虚拟数据读取是错误的做法? - Josh Heitzman
    5
    感觉不太对劲,我想可能会有一些更优雅的方法我不知道。但如果这是唯一的解决方案,并且有经验的人可以确认没有其他方法,那我就接受它了。 - user1372494
    1
    我在应用程序池在非活动期间(由于低流量)关闭的问题上遇到的一个解决方法是创建一个服务,在一定时间间隔内向你的某个页面发出请求。这可以防止在第一次请求时出现长时间延迟之前,应用程序池重新启动。或者你可以使用像 www.pingalive.com 这样的免费服务来ping你的域名/ip。这也有助于在它们过期之前防止缓存的对象被清除。 - hatsrumandcode
    @CStyle 你可以在内部实现这个功能,而不必创建一个单独的服务。只需创建一个带有回调函数的缓存条目,该函数只是进行请求并再次插入缓存条目。您可以指定时间等参数。我在一个项目中成功地使用了它,当然第一个请求需要以某种方式得到保证 - 预热模块(启用预加载/自动启动)是一个很好的方法。 - user1372494
    显示剩余6条评论
    5个回答

    54
    • 如何在任何时候都实现Entity Framework高可用性最佳方法?

    您可以采用预生成视图和静态编译查询的混合方式。

    静态CompiledQuerys很好,因为它们编写起来快速简单,并有助于提高性能。但是,使用EF5时不需要编译所有查询,因为EF将自动编译查询。唯一的问题是这些查询可能会在缓存被清除时丢失。因此,对于那些很少发生但代价昂贵的查询,您仍然希望引用自己编译的查询。如果将这些查询放入静态类中,则它们将在首次使用时进行编译。这对于某些查询可能太晚了,因此您可能希望在应用程序启动期间强制编译这些查询。

    另一个可能性是预生成视图,就像您提到的那样。特别是对于那些编译时间非常长且不会更改的查询。这样,您可以将性能开销从运行时移动到编译时。此外,这不会引入任何延迟。但是,当然,这种变化会传递到数据库中,因此处理起来不那么容易。代码更灵活。

    不要使用大量的TPT继承(这是EF中的一般性能问题)。也不要构建过深或过宽的继承层次结构。仅对某些类特定的2-3个属性可能不足以要求自己的类型,但可以作为现有类型的可选(可空)属性进行处理。

    不要长时间保留单个上下文。每个上下文实例都有自己的一级缓存,随着其增长而减慢性能。上下文创建很便宜,但是缓存的实体状态管理可能变得昂贵。其他缓存(查询计划和元数据)在上下文之间共享,并将随AppDomain一起结束。

    总之,您应该确保频繁分配上下文并仅短时间使用它们,以便您可以快速启动应用程序,编译很少使用的查询,并为经常使用且性能关键的查询提供预生成视图。

    • 什么情况下实体框架会再次“冷却”?(重新编译、回收、IIS重新启动等)

    基本上,每当你失去你的AppDomain时。 IIS每 29小时 执行一次重启,所以你永远无法保证你将拥有这些实例。此外,在一段时间没有活动后,AppDomain也会关闭。您应该尽快重新启动。也许您可以异步地进行一些初始化操作(但要注意多线程问题)。您可以使用定时任务,在没有请求时调用应用程序中的虚拟页面,以防止AppDomain死亡,但最终还是会发生。

    我还假设当您更改配置文件或更改程序集时,将进行重启。


    @Andreas 实际上,即使使用静态编译的查询,第一次运行也太长了。除了在应用程序启动时进行虚拟数据读取以预热,生成和验证模型之外,还有其他方法可以预热吗? - manishKungwani
    @Andreas,Entity Framework 5需要吗?如果在EF5上使用它有什么不同(我的意思是速度仍然很慢还是稍微好一些或者没有区别)? - qakmak
    静态编译查询很好,因为它们快速且易于编写,并有助于降低性能。降低性能? - Mathemats

    8
    如果您希望在所有调用中获得最大性能,则应仔细考虑架构。例如,当应用程序加载时,预缓存经常使用的查找结果到服务器RAM中可能是有意义的,而不是在每个请求上使用数据库调用。该技术将确保常用数据的最小应用程序响应时间。但是,您必须确保具有良好的过期策略或在影响缓存数据的更改时始终清除缓存,以避免并发问题。
    通常,您应该努力设计分布式架构,仅在本地缓存信息变为陈旧或需要进行事务处理时才需要基于IO的数据请求。任何"通过网络"的数据请求通常需要比本地内存缓存检索时间长10-1000倍。这一个事实通常使"冷数据 vs. 热数据"的讨论相对于"本地 vs. 远程"数据问题而言显得微不足道。

    这是一个很好的观点,我经常在对实体框架原始性能感到兴奋时忽略它。我会进一步研究缓存的原则。然而,关于实体框架的“冷 vs. 热”,我仍然希望更好地理解。 - user1372494
    2
    仅凭这一点,关于“冷数据 vs. 热数据”的讨论往往相对于“本地 vs. 远程”数据问题而言不那么重要。不完全是这样的。如果您没有将其缓存到本地(最初您不会),您仍需要访问 EF 并遭受初始化痛苦以启动缓存。与您的缓存未初始化的相同位置,EF 也未初始化。因此,如果唯一的问题是 EF 初始化时间,则添加缓存层可能无济于事,但它将增加另一层复杂性... - MikeJansen

    7

    一般提示。

    • 进行严格的日志记录,包括访问内容请求时间
    • 在初始化应用程序时执行虚拟请求以预热非常缓慢的请求,这些请求是从上一步中获取的。
    • 除非确实存在问题,请勿优化,与应用程序的使用者沟通并询问。如果只是为了找出需要优化的内容,请习惯于拥有持续反馈循环。

    现在解释一下为什么虚拟请求不是错误的方法

    • 更少的复杂性-您正在以一种方式预热应用程序,无论框架发生何种变化都能正常工作,并且您不需要弄清楚可能的 API / 框架内部来完成正确的方法
    • 更大的覆盖面-您正在同时预热与缓慢请求相关的所有缓存层。

    解释何时缓存会"失效"。

    这发生在框架的任何层次应用缓存时,详见性能页面顶部的描述。

    • 每当缓存必须在潜在更改之后进行验证时,这可能是超时或更智能的(即缓存项的更改)。
    • 当缓存项被驱逐时,其算法在您链接的性能文章中的"缓存驱逐算法"一节中有描述,但简而言之:
      • LFRU(最近最不频繁使用)缓存,根据命中次数和年龄限制为800个项目。

    您提到的其他事情,特别是重新编译和重新启动IIS,会清除内存中的部分或全部缓存。


    这是另一个有用的答案,非常感谢。 - user1372494

    4
    如您所述,只需使用“预生成的视图”。从您提供的链接中可以看出:“当生成视图时,它们也被验证。从性能角度来看,视图生成的绝大部分成本实际上是视图验证。”这意味着性能损失将在构建模型程序集时发生。然后,您的上下文对象将跳过“冷查询”,并在上下文对象生命周期以及随后的新对象上下文中保持响应。
    执行无关查询除了消耗系统资源外没有其他用途。
    简单的步骤如下:
    1. 跳过所有额外的预生成视图工作 2. 创建您的对象上下文 3. 执行那个不相关的查询 4. 然后只需保留您的对象上下文的引用,直到您的进程结束(不推荐)。

    3
    请查看:使用 T4 模板生成视图 - kzfabi

    1

    我对这个框架没有经验。但在其他情况下,例如Solr,除非您可以缓存整个数据库(或索引),否则完全的虚拟读取将没有太大用处。

    更好的方法是记录查询,从日志中提取最常见的查询,并使用它们进行预热。只需确保不记录预热查询或在继续之前从日志中删除它们即可。


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接