【问题标题】:Entity Framework - how to avoid query recompilation?实体框架 - 如何避免查询重新编译?
【发布时间】:2016-01-18 21:56:26
【问题描述】:

我正在优化我们的 Entity Framework 代码,目前我遇到了一个不知道如何解决的问题。

我们正在使用 Azure SQL + 代码优先实体框架 6.1.3 + Asp.net Web Api v2。端点托管在云端,我正在使用它进行测试。

我有一个 API 操作,用于获取过滤、排序和分页的数据。这是我处理数据的简化代码:

var entities = DbContext.Services
        .Include(q => q.Consumer.Building.Address.Country)
        .Include(q => q.Consumer.Building.Address.State);

entities = entities.OrderBy(x => x.Consumer.RegisteredAt);

entities = entities.Where(x => x.IsDeleted == false); 
entities = entities.Where(x => userId.HasValue ? x.Owner.Id == userId ? true); //this part comes from deep internals, so I cannot change it quickly
var page = entities = entities.Skip(skip).Take(take).ToList();
var count = entities.Count();

var dtoPage = Mapper.Map<IEnumerable<ServiceDto>>(page);

return Page<ServiceDto>(dtoPage, count);

所以代码没有什么特别之处——不使用IN 子句等,只是简单的过滤、排序和分页。

问题: 调用此方法执行时间不稳定。我使用了一个简单的脚本来调用 API 端点,该端点使用不同的参数调用此代码:它依次获取 0..5 页 200 次。对于每个呼叫(第​​一个呼叫除外),我预计呼叫时间少于 300 毫秒。 但是:在 1200 次调用中,有 36 次调用时间超过了 1 秒 - 就像在此期间再次重新编译了查询一样。平均调用时间为 250ms,但有时会飙升至 1000ms,相差 4 倍。

测试的常见行为是:

  1. 第一次查询很慢

  2. 第二次查询更快,但仍然比休息慢

  3. 除了不时出现的峰值之外,其余查询非常稳定。

除了我之外没有人使用过测试环境,所以这不是负载问题,我可以在任何环境中重现它,即使是在快速的本地 PC - i5 + 8gb RAM + SSD 上。

测试之间的延迟也很小 -

所以简而言之,我的问题是: 这个问题是怎么来的?

  1. 这真的是重新编译问题吗?如果不是,问题的可能根源是什么?
  2. 是否有关于 Entity Framework 6 的查询缓存失效的文档?
  3. 有没有办法让查询在缓存中保留更长的时间,这样如果我现在和 30 分钟后查询该方法,它就不必重新编译它?

【问题讨论】:

标签: c# entity-framework azure entity-framework-6 azure-sql-database


【解决方案1】:

这真的是重新编译的问题吗,

我认为这不是重新编译问题。但是您可以通过在 SkipTake 调用中传递 lambdas 来减轻重新编译。见4.2 Using functions that produce queries with constants

如果不是,问题的可能根源是什么?

AFAIK SqlAzure 是共享环境。这可能是您的问题的原因。

是否有关于 Entity Framework 6 的查询缓存失效的文档? 有没有办法让查询在缓存中保留更长时间,这样如果我现在和 30 分钟后查询该方法,它就不必重新编译它?

您的实体框架查询将在 AppDomain 中停留在内存中,只要服务器不需要内存,sql 查询计划将停留在 Sql Server 内存中。带有 lambda 的 SkipTake 也将有助于解决这个问题。 见同一页:

"In EF6 these methods have a lambda overload that effectively makes the
cached query plan reusable because EF can capture variables passed to
these methods and translate them to SQLparameters. This also helps
keep the cache cleaner since otherwise each query with a different
constant for Skip and Take would get its own query plan cache entry"

【讨论】:

  • 跳过/参加对我很有帮助,谢谢。尽管在应用程序域处于活动状态时查询保留在内存中的部分 - 如果我逐页查询,查询执行时间很好。如果我等待 3 分钟 - 这非常低并且看起来不像默认超时 - 我的 api 调用时间跳到未编译查询执行的值 - 这是一个不同的问题吗?
  • @raderick 这可能有助于查看 azure 数据库的查询存储中的内容:sqlmag.com/sql-server/query-store-azure-sql-database
  • 这真的很有帮助,谢谢!我会试试这个,希望它真的对我有帮助!现在,我会将您的答案标记为有用的答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-26
  • 1970-01-01
  • 2016-01-14
  • 1970-01-01
相关资源
最近更新 更多