118

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

前言:

我最近一直在对 Entity Framework 进行大量研究,一直困扰我的是它在查询未预热时的性能,即所谓的冷查询。

我浏览了 Entity Framework 5.0 的性能注意事项文章。作者介绍了查询和查询的概念以及它们之间的区别,我自己也注意到了这一点,但并不知道它们的存在。在这里可能值得一提的是,我背后只有六个月的经验。

现在我知道如果我想在性能方面更好地理解框架,我还可以研究哪些主题。不幸的是,Internet 上的大多数信息都已过时或带有主观性,因此我无法找到有关查询主题的任何其他信息。

到目前为止,我基本上注意到的是,每当我必须重新编译或回收命中时,我的初始查询都会变得非常慢。正如预期的那样,任何后续数据读取都很快(主观)。

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

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

  • 按照建议提前生成我的意见。
  • 最终将我的模型移动到一个单独的组件中。

通过常识,我考虑做的事情可能是错误的方法

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

问题:

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

5 回答 5

54
  • 在任何时候在我的实体框架上获得高可用性的最佳方法是什么?

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

静态CompiledQuery很好,因为它们编写起来既快速又容易,并且有助于提高性能。但是,对于 EF5,没有必要编译所有查询,因为 EF 会自动编译查询本身。唯一的问题是这些查询在缓存被扫描时可能会丢失。因此,您仍然希望保留对您自己编译的查询的引用,这些查询仅发生非常罕见但代价高昂。如果您将这些查询放入静态类中,它们将在首次需要时进行编译。对于某些查询,这可能为时已晚,因此您可能希望在应用程序启动期间强制编译这些查询。

正如您提到的,预生成视图是另一种可能性。特别是对于那些需要很长时间编译并且不会改变的查询。这样,您就可以将性能开销从运行时转移到编译时。这也不会引入任何滞后。但是当然这种变化会传递到数据库,所以处理起来并不容易。代码更灵活。

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

不要长时间坚持单一的上下文。每个上下文实例都有自己的一级缓存,当它变大时会降低性能。上下文创建很便宜,但上下文缓存实体内的状态管理可能会变得很昂贵。其他缓存(查询计划和元数据)在上下文之间共享,并将与 AppDomain 一起消失。

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

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

基本上,每次您丢失 AppDomain 时。IIS 每29 小时执行一次重新启动,因此您永远无法保证您的实例会存在。同样在一段时间没有活动后,AppDomain 也会关闭。你应该尝试再次快速上来。也许您可以异步进行一些初始化(但要注意多线程问题)。您可以在没有请求阻止 AppDomain 死亡时使用在应用程序中调用虚拟页面的计划任务,但它最终会死亡。

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

于 2012-11-28T12:58:41.060 回答
8

如果您正在寻找所有调用的最佳性能,您应该仔细考虑您的架构。例如,当应用程序加载时,在服务器 RAM 中预先缓存常用的查找而不是在每个请求上使用数据库调用可能是有意义的。这种技术将确保常用数据的应用程序响应时间最短。但是,您必须确保有一个行为良好的过期策略,或者在进行影响缓存数据的更改时始终清除缓存,以避免并发问题。

通常,您应该努力设计分布式架构,以便仅在本地缓存的信息变得陈旧或需要进行事务处理时才需要基于 IO 的数据请求。任何“在线”数据请求的检索时间通常比本地内存缓存检索长 10-1000 倍。与“本地与远程”数据问题相比,仅这一事实就经常使关于“冷数据与热数据”的讨论变得无关紧要。

于 2012-11-27T14:13:09.000 回答
7

一般提示。

  • 执行严格的日志记录,包括访问的内容请求时间
  • 在初始化您的应用程序以热启动您从上一步中提取的非常慢的请求时执行虚拟请求。
  • 除非它是一个真正的问题,否则不要费心优化,与应用程序的消费者沟通并询问。如果只是为了弄清楚什么需要优化,就可以轻松地拥有一个持续的反馈循环。

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

  • 降低复杂性- 您正在以一种无论框架变化如何都能正常工作的方式来预热应用程序,并且您不需要找出可能时髦的 API/框架内部结构来以正确的方式进行操作。
  • 更大的覆盖范围- 您正在同时预热与慢速请求相关的所有缓存层。

解释缓存何时“冷”。

这发生在框架中应用缓存的任何层,性能页面顶部有一个很好的描述。

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

于 2012-11-29T05:43:26.110 回答
4

正如您所说,使用“预先生成的视图”这就是您真正需要做的。

从您的链接中提取:“生成视图时,它们也会被验证。从性能的角度来看,视图生成的绝大部分成本实际上是视图的验证”

这意味着在您构建模型装配时会发生性能冲击。然后,您的上下文对象将跳过“冷查询”并在上下文对象生命周期以及后续的新对象上下文期间保持响应。

执行不相关的查询只会消耗系统资源。

捷径...

  1. 跳过预生成视图的所有额外工作
  2. 创建您的对象上下文
  3. 触发那个甜蜜的无关查询
  4. 然后在您的过程中保留对您的对象上下文的引用(不推荐)。
于 2012-11-14T20:37:14.710 回答
1

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

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

于 2012-11-23T15:13:14.850 回答