实体框架 - 如何避免查询重新编译?

时间:2016-01-18 13:21:02

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

我正在努力优化我们的实体框架代码,目前我正面临一个我不确定如何解决的问题。

我们正在使用 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毫秒。 BUT:来自1200个总呼叫,36个呼叫时间超过1秒 - 就像在此期间再次重新编译查询一样。通话的平均时间 250ms ,但有时飙升到1000ms,这是4倍的差异。

测试的常见行为是:

  1. 第一次查询很慢

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

  3. 休息查询非常稳定,除了偶尔出现一些峰值。

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

    测试之间的延迟也很小 -

    所以我的问题很简单: 这个问题来自什么?

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

1 个答案:

答案 0 :(得分:4)

  

这真的是重新编译问题,

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

  

如果没有,问题的可能来源是什么?

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

  

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

只要服务器不需要内存,只要AppDomain,sql查询计划将保留在Sql Server内存中,您的实体框架查询将保留在内存中。带有lambdas的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"