实体框架查询速度很慢,但是SqlQuery中的相同SQL速度很快


93

我看到一些非常奇怪的性能,它们与使用.NET Framework版本4的Entity Framework Code-First进行的非常简单的查询有关。LINQ2Entities查询如下所示:

 context.MyTables.Where(m => m.SomeStringProp == stringVar);

这将花费3000毫秒以上的时间来执行。生成的SQL看起来非常简单:

 SELECT [Extent1].[ID], [Extent1].[SomeStringProp], [Extent1].[SomeOtherProp],
 ...
 FROM [MyTable] as [Extent1]
 WHERE [Extent1].[SomeStringProp] = '1234567890'

通过Management Studio运行时,该查询几乎立即运行。当我更改C#代码以使用SqlQuery函数时,它将在5到10毫秒内运行:

 context.MyTables.SqlQuery("SELECT [Extent1].[ID] ... WHERE [Extent1].[SomeStringProp] = @param", stringVar);

因此,使用完全相同的SQL,在两种情况下,生成的实体都会进行更改跟踪,但是两者之间的性能差异很大。是什么赋予了?


2
我希望您看到初始化延迟-可能是查看编译。参见MSDN:Performance Considerations for Entity Framework 5
Nicholas Butler

我已经尝试过预先生成视图,但似乎没有帮助。同样,在慢速查询之前运行另一个EF查询以排除初始化问题。即使在第一个查询过程中发生了上下文热身,新查询仍快速运行,而慢速查询仍然运行缓慢。
Brian Sullivan

1
@marc_s-否,SqlQuery将返回一个完全实例化和更改跟踪的实体实例。请参阅msdn.microsoft.com/en-us/library/…–
Brian Sullivan

为您的EF查询生成的SQL是否实际上是内联参数值或使用参数?这不应该影响单个查询的查询速度,但是随着时间的推移,可能会导致服务器中的queryplan膨胀。
Jim Wooley 2013年

您是否尝试过两次/多次运行同一查询?第二次运行需要多长时间?您是否在.NET Framework 4.5上进行了尝试-.NET Framework 4.5中有一些与EF相关的性能改进可能会有所帮助。
Pawel

Answers:


96

找到了。原来这是SQL数据类型的问题。SomeStringProp数据库中的列是varchar,但是EF假定.NET字符串类型是nvarchars。在查询过程中,数据库进行比较需要花费很长时间。我认为EF教授在这里误导了我,正在运行的查询的更准确表示如下:

 SELECT [Extent1].[ID], [Extent1].[SomeStringProp], [Extent1].[SomeOtherProp],
 ...
 FROM [MyTable] as [Extent1]
 WHERE [Extent1].[SomeStringProp] = N'1234567890'

因此,最终的解决方法是注释代码优先模型,以指示正确的SQL数据类型:

public class MyTable
{
    ...

    [Column(TypeName="varchar")]
    public string SomeStringProp { get; set; }

    ...
}

1
很好的调查。您的查询存在“隐式转换”的问题,如此处所述:brentozar.com/archive/2012/07/…–
Jaime

节省了我几个小时的调试时间。这正是问题所在。
科迪

1
就我而言,我将EDMX与旧式数据库配合使用,该数据库可varchar用于所有内容,而实际上这就是问题所在。我想知道是否可以让EDMX将varchar用于所有字符串列。
Alisson

1
伟大的发现人。但是@Jaime对于数据库优先方法应该做什么,因为从数据库更新EF模型后,一切(例如db Models上的数据注释)都会消失。
瑙曼·汗

将其设置为我的主页一段时间,这样我就可以重温一下再次找到这么好的答案的兴奋。谢谢!!!
OJisBad

43

减慢我在EF中进行查询的原因是将不可为空的标量与可为空的标量进行比较:

long? userId = 10; // nullable scalar

db.Table<Document>().Where(x => x.User.Id == userId).ToList() // or userId.Value
                                ^^^^^^^^^    ^^^^^^
                                Type: long   Type: long?

该查询花费了35秒。但是这样的微小重构:

long? userId = 10;
long userIdValue = userId.Value; // I've done that only for the presentation pursposes

db.Table<Document>().Where(x => x.User.Id == userIdValue).ToList()
                                ^^^^^^^^^    ^^^^^^^^^^^
                                Type: long   Type: long

给出了令人难以置信的结果。只需50毫秒即可完成。这很可能是EF中的错误。


13
这太奇怪了
Daniel Cardenas

1
我的天啊。当使用接口IUserId.Id导致我出现问题时,这显然也可能发生,但是首先将Id映射到整数可以工作...我现在必须检查我的100.000行应用程序中的所有查询吗?
Dirk Boer

是否已报告此错误?它仍然是最新版本6.2.0
Dirk Boer

2
EF Core中也存在同样的问题。感谢您找到这个!
Yannickv

另一种建议是在放入LINQ表达式之前先处理变量。否则,生成的sql将变得更长和更慢。我在LINQ表达式中包含Trim()和ToLower()时感到很烦。
samheihey


4

我遇到了同样的问题(从SQL管理器执行查询时查询很快),但是从EF执行时超时到期。

原来,实体(从视图创建)具有错误的实体键。因此,该实体具有使用相同键的重复行,我想它必须在背景上进行分组。


3

我还遇到了一个复杂的ef查询。对我来说,将6秒的ef查询减少到它生成的2秒以下的sql查询的一个解决方法是关闭延迟加载。

要找到此设置(ef 6),请转到.edmx文件,然后在“属性”->“代码生成”->“启用延迟加载”中查找。设置为false。

对我来说,性能有很大的提高。


4
太酷了,但与发布者问题无关。
杰斯·瑞亚

2

我也有这个问题。事实证明,在我的案例中,罪魁祸首是SQL-Server 参数嗅探

我的问题实际上是由于参数嗅探引起的第一个线索是,在“ set arithabort off”或“ set arithabort on”下运行查询在Management Studio中产生了截然不同的执行时间。这是因为ADO.NET默认情况下使用“关闭arithabort”,而Management Studio默认使用“打开arithabort”。查询计划缓存根据此参数保留不同的计划。

我禁用了查询的查询计划缓存,您可以在此处找到解决方案。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.