【问题标题】:Same SQL query is fast or slow depending on execution context相同的 SQL 查询是快还是慢取决于执行上下文
【发布时间】:2017-12-22 10:54:10
【问题描述】:

我在研究典型 ASP.NET MVC 应用程序的缓慢视图时遇到了一个奇怪的现象。其中一个查询无缘无故地运行缓慢。有问题的 LINQ 查询如下所示(DbDbContext):

var testResults = Db.CustomTestResults
    .Include(tr => tr.TestMachine.Platform)
    .Include(tr => tr.TestCase)
    .Include(tr => tr.CustomTestResultAnalysis.Select(tra => tra.AnalysisOutcomeData))
    .Where(tr => tr.CustomTestBuildId == testBuild.Id)
    .ToList()
    .AsReadOnly();

其实没什么特别的。根据过滤器,查询结果集的大小可能会有所不同,最多 10 到 10000 条记录。

从 SSMS 执行的 SQL 生成的查询(由 LINQ 调试日志捕获)运行速度很快,最大的集合大约需要 2 秒,而较小的集合则不到 1 秒。然而,随后由 IIS 运行的奇怪事情发生了。查询开始以慢 1/100 倍的速度运行。较小的需要大约 10 秒来执行,较大的由于查询执行超时而失败。我不确定是否有任何其他查询受到影响,但这只是处理大型数据集,所以最明显的是注意到这个问题。

由于这还不够令人困惑,所以不久前,同样的代码可以按预期完美运行。所以这个bug似乎是由一些外部因素引起的。数据库为 SQL Server 2014 SP2,EF 为 v6.2,IIS 7.5。

如果我能在哪些领域以及如何进一步调查这方面的任何想法,我将不胜感激。

【问题讨论】:

  • 使用 XEvent Profiler 或(不推荐使用的)SQL Server Profiler 来捕获围绕 EF 查询执行和 实际 执行计划的事件。 EF 可能设置了意外设置,使用批处理加载数据或使用慢游标——它不应该这样做。扩展事件可以捕获比 SQL Server 分析器更多的东西
  • 尝试使用像 Dappert 这样的 microORM 来报告查询,以避免 EF 做出这种不幸的决定。如果您还没有禁用报告查询的更改跟踪,那么您应该禁用更改跟踪,尽管这不能解释数量级延迟
  • 顺便说一句,我遇到了同样的问题。几个月后,使用 SSMS 运行良好的查询开始使用 EF 花费更长的时间。更改跟踪禁用。
  • @PanagiotisKanavos 实际上这很有意义,谢谢。这就解释了为什么性能会无缘无故地突然改变。不幸的是,我没有运行分析器的特权。有什么方法可以规范 EF 使用哪些查询选项,并以某种方式强制它们?
  • 如果你不知道发生了什么,你就无法修复它。在 SQL Server 2014 中,跟踪是通过扩展事件完成的,而不是探查器。您不能从 SSMS 启动 XE 会话吗?如果您无法在生产环境中执行此操作,请在测试环境中尝试。事件的顺序将是相同的

标签: sql-server entity-framework linq


【解决方案1】:

事实证明,问题出在 SQL Server 优化中,它在多次运行类似查询后开始工作一段时间。此问题可以通过对原始查询的任何不相关更改来检测,这会在一段时间内修复性能。

controlling query command options 可以适当缓解这种行为。 EF 的解决方案之一是demonstrated here

作为一个临时的“quick-and-dirty”解决方案,我每次都使用这种方法来随机化查询,从而防止 SQL Server 引擎进行优化:

private static IQueryable<CustomTestResult> RandomizeQuery(IQueryable<CustomTestResult> query)
{
    const int minConditions = 1;
    const int maxConditions = 5;
    const int minId = -100;
    const int maxId = -1;

    var random = new Random();
    var conditionsCount = random.Next(minConditions, maxConditions);
    for (int i = 0; i < conditionsCount; i++)
    {
        var randomId = random.Next(minId, maxId);
        query = query.Where(test => test.Id != randomId);
    }

    return query;
}

【讨论】:

    【解决方案2】:

    由于 SQL 没有更改,但根据您运行的平台会出现问题,我将从您的设置开始。 Erland Sommarskog 写了一个关于为什么和如何做的很好的参考:http://www.sommarskog.se/query-plan-mysteries.html

    这很长,但我想你会在那里找到答案。

    【讨论】:

    • 这应该是一条评论,因为它没有回答问题。 database 是相同的,因此设置无关紧要。 EF 查询的实际 执行计划可能不同,也可能不同。不过,Erland 的文章并没有谈到这一点
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-01-10
    • 2013-03-23
    • 2015-01-06
    • 2021-12-13
    • 1970-01-01
    • 2015-10-22
    • 1970-01-01
    相关资源
    最近更新 更多