当前位置: 首页 > 面试题库 >

Entity Framework生成的sp_executesql与SSMS中的直接查询之间的主要性能差异

微生令雪
2023-03-14
问题内容

我正在使用Entity Framework进行较大的查询。最近,由于超时异常,此查询失败。

当我开始调查此问题时,我使用LinqPad并直接在SSMS中复制SQL输出并运行查询。该查询将在1秒内返回

然后查询看起来像(仅用于说明,实际查询要大得多)

DECLARE @p__linq__0 DateTime2 = '2017-10-01 00:00:00.0000000'
DECLARE @p__linq__1 DateTime2 = '2017-10-31 00:00:00.0000000'

SELECT 
    [Project8].[Volgnummer] AS [Volgnummer], 
    [Project8].[FkKlant] AS [FkKlant], 
    -- rest omitted for brevity

现在,我使用SQL事件探查器捕获了发送到服务器的真实SQL。该查询完全相同,不同之处在于该查询被封装在对的调用中sp_executesql。像这样:

exec sp_executesql N'SELECT 
    [Project8].[Volgnummer] AS [Volgnummer], 
    [Project8].[FkKlant] AS [FkKlant], 
    -- rest omitted for brevity  
    ',N'@p__linq__0 datetime2(7),@p__linq__1 datetime2(7)',
        @p__linq__0='2017-10-01 00:00:00',@p__linq__1='2017-10-31 00:00:00'

当我在SSMS中复制/粘贴此查询时,它会运行60秒,因此在默认设置下从EF使用时会导致超时!

我无法确定为什么会出现这种差异,因为这是相同的查询,唯一的是,它的执行方式不同。

我读了很多关于EF为什么使用sp_executesql的知识,并且我理解为什么。我还读到sp_executesql与EXEC有所不同,因为它利用了queryplan缓存,但是我不明白为什么SQL优化器在为sp_executesql版本创建高效的查询计划时却如此困难,而它却能够创建高效的查询计划。直接查询版本。

我不确定完整的查询本身是否会增加问题。如果是这样,请告诉我,然后进行编辑。


问题答案:

多亏了提供的注释,我可以完成两件事:

  • 我现在了解了查询计划以及查询中的参数嗅探和变量之间的区别
  • 我实现了在需要时DbCommandInterceptor添加OPTION (OPTIMIZE FOR UNKNOWN)到查询中的功能。

通过将实现添加到,可以拦截由Entity Framework编译的SQL查询,然后再将其发送到服务器DbInterception

这样的实现很简单:

public class QueryHintInterceptor : DbCommandInterceptor
{
    public override void ReaderExecuting(DbCommand command, 
        DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
        queryHint = " OPTION (OPTIMIZE FOR UNKNOWN)";
        if (!command.CommandText.EndsWith(queryHint))
        {
            command.CommandText += queryHint;
        }

        base.ReaderExecuting(command, interceptionContext);
    }
}
// Add to the interception proces:
DbInterception.Add(new QueryHintsInterceptor());

由于Entity Framework也缓存查询,因此我检查是否已添加优化。

但是这种方法将拦截所有查询,而且显然不应该这样做。随着对的DbCommandInterceptionContext访问,DbContext我为我添加了一个具有单个属性(ISupportQueryHints)的接口,DbContext当查询需要此接口时,我将该接口设置为优化。

现在看起来像这样:

 public class QueryHintInterceptor : DbCommandInterceptor
{
    public override void ReaderExecuting(DbCommand command, 
        DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
        var dbContext =
            interceptionContext.DbContexts.FirstOrDefault(d => d is ISupportQueryHints) as ISupportQueryHints;

        if (dbContext != null)
        {
            var queryHint = $" OPTION ({dbContext.QueryHint})";
            if (!command.CommandText.EndsWith(queryHint))
            {
                command.CommandText += queryHint;
            }
        }

        base.ReaderExecuting(command, interceptionContext);
    }
}

在需要的地方可以用作:

public IEnumerable<SomeDto> QuerySomeDto()
{
    using (var dbContext = new MyQuerySupportingDbContext())
    {
        dbContext.QueryHint = "OPTIMIZE FOR UNKNOWN";
        return this.PerformQuery(dbContext);
    }
}

因为我的应用程序利用了围绕命令和查询的基于消息的体系结构(如此处所述),所以我的实现由需要优化的查询处理程序周围的装饰器组成。必要时,此装饰器将查询提示设置为DbContext。但是,这是一个实现细节。基本思想保持不变。



 类似资料:
  • 问题内容: 我有两个我以为是同一意思的查询,但是我一直得到不同的结果,我希望有人可以解释这些差异如何: 1。 2。 查询的重点是查找表1中的行和val = 0而不是表2中的行。 我也使用sql server 2008,但是我怀疑这是否重要。 问题答案: 在考虑左联接时,应将它们视为具有3个概念阶段。 应用联接过滤器 左侧的行又被添加回 应用where子句。 然后,您将看到为什么得到不同结果的原因。

  • 我刚开始使用普罗米修斯,我正在尝试理解一些事情。 我有一个Java的web应用程序,它公开了一个叫做“my_counter”的计数器。 下面的查询有什么区别? 如果我在1小时的相同时间间隔内运行这些查询,为什么两个查询返回不同的结果? 我试图实现的是看看计数器在一个可配置的时间段内增加了多少。 多谢了。 附注。我实际上使用Grafana来运行查询并选择时间范围。

  • 上次,我发现了Java8及以上版本函数式编程的难点,并在Collectors类中发现了一个静态方法。 我们有一个类员工像: 假设我们有一个类的POJO列表,并且我们希望接收一个所有员工姓名的列表。我们有两种方法,比如: 我知道第一种方法在上使用终端操作,而第二种方法在上使用中间操作,但我想知道第一种方法的性能是否比第二种方法差,反之亦然。如果您能解释第一种情况的潜在性能下降,当我们的数据源(emp

  • 问题内容: 有一个共识,使用接口比使用类更好。我当然同意:库法接受ArrayList替代的List将是一个废话。 也有一个共识,就是性能总是一样的。在这里,我的基准要求不同。 接口和抽象类结果都有1到4种实现。当使用两个以上的实现时,性能开始出现差异。我正在寻找这种行为的解释(以及错误共识的由来)。 问题答案: 有一个共识,使用接口比使用类更好。 这太简单了。接口和抽象类都 具有彼此之间的优势。

  • 我计划使用NodeJS api查询大量的数据。使用流api(https://cloud.google.com/nodejs/docs/reference/firestore/0.13.x/Query?#stream)代替常规的查询获取(https://cloud.google.com/nodejs/docs/reference/firestore/0.13.x/Query?#get)有什么好处吗?

  • 问题内容: 我正在运行以下代码,但有时在运行时会出现某种并发异常。 我对其进行了重构以解决并发问题,但这确实使我提出了一个问题。如果将for构造更改为Iterator模式,性能会有所不同吗?foreach构造和Iterator类之间的访问级别有什么区别? 问题答案: 区别主要在于语法糖,不同之处在于可以从迭代中删除项目。从技术上讲,增强的循环使您可以循环遍历所有,其中至少包括s和数组。 不必担心性