LINQ查询为什么变得非常缓慢?

4

我有一个非常简单的LINQ查询:

List<table> list = ( from t in ctx.table
                     where
                     t.test == someString
                     && t.date >= dateStartInt
                     && t.date <= dateEndInt
                     select t ).ToList<table>();

被查询的表有大约三千万行数据,但是列“test”和“date”已经建立了索引。当应该返回大约5000行时,需要几分钟才能完成查询。
我也检查了LINQ生成的SQL命令。如果我在SQL Server上运行该命令,只需要2秒钟就可以完成。
这里LINQ出了什么问题?这只是一个非常简单的查询,没有任何联接。
以下是SQL Profiler显示的查询:
exec sp_executesql N'SELECT [t0].[test]
FROM [dbo].[table] AS [t0]
WHERE ([t0].[test] IN (@p0)) AND ([t0].[date] >= @p1) 
AND ([t0].[date] <= @p2)',
N'@p0 nvarchar(12),@p1 int,@p2 int',@p0=N'123test',@p1=110801,@p2=110804

编辑:

真的很奇怪。在测试时,我注意到现在速度快多了。LINQ查询现在需要大约20000行数据3秒钟,这是相当不错的。

更加令人困惑的是: 我们的生产服务器上也有同样的情况。一小时前它非常慢,现在又变得很快了。由于我是在开发服务器上进行测试的,所以没有对生产服务器做任何更改。我能想到的唯一可能出现问题的原因是两个服务器都是虚拟化的,并与许多其他服务器共享SAN。

我该如何确定这是否是问题的原因?


SQL Server 会缓存查询,因此您可能无法进行公平的测试。 - Graham Clark
你看过执行计划了吗?索引是否碎片化了? - Phill
2
使用SQL Server Profiler查看,找出真正执行的查询语句。 - Heinzi
我不理解的是,我们有另一个拥有大约6亿行的巨型表格,但对该表格的查询只需一秒钟(使用LINQ)。这种差异来自哪里?我从未使用过SQL Server Profiler,但现在我会尝试一下。 - Dave
table 的构造函数里有什么内容?它的属性设置器中是否有任何操作? - Tim Rogers
"table" 只是自动生成的 LINQ-to-SQL 类。 - Dave
2个回答

2
如果我要猜测,我会说"参数嗅探"很可能发生了,即它已经构建并缓存了一个基于一组参数的查询计划,这对于您当前的参数值非常不利。您可以使用常规TSQL中的"OPTION(OPTIMIZE FOR UNKNOWN)"来解决这个问题,但没有LINQ-to-SQL/EF的方法来暴露这个问题。
我的计划是:
1. 使用分析证明时间是在查询中被浪费的(而不是材料化等)。 2. 确认后,考虑使用直接的TSQL方法来调用。
例如,在LINQ-to-SQL中,可以使用"ctx.ExecuteQuery(tsql, arg0,...)"将原始的TSQL语句发送到服务器(参数为"{0}"等,类似于"string.Format")。个人而言,我更倾向于使用"dapper"——非常相似的用法,但速度更快的材质器(但它不支持"EntityRef<>"等用于懒加载值的功能,这通常是一个坏习惯,因为它会导致N+1)。
也就是说,(使用dapper):
List<table> list = ctx.Query<table>(@"
   select * from table
   where test == @someString
   and date >= @dateStartInt
   and date <= @dateEndInt
   OPTION (OPTIMIZE FOR UNKNOWN)",
new {someString, dateStartInt, dateEndInt}).ToList();

或者(LINQ-to-SQL):
List<table> list = ctx.ExecuteQuery<table>(@"
   select * from table
   where test == {0}
   and date >= {1}
   and date <= {2}
   OPTION (OPTIMIZE FOR UNKNOWN)",
someString, dateStartInt, dateEndInt).ToList();

@Dave,你们正在使用相同的值进行测试吗?好的... testdate 的完整数据库类型是什么?例如,test 是否被定义为 nvarchar(100)char(20) 等类型? - Marc Gravell
是的,完全一样。我运行了几次并使用StopWatch比较了持续时间。测试为“nvarchar(12)”,日期为“int”。 - Dave
@Dave,它们是常规列吗?计算值吗?持久化计算值吗?……? - Marc Gravell
只是普通的列。实际上,第二个查询需要完成的时间是正常的linq查询的两倍。我用返回4500行的值进行了测试。 - Dave
这真的很奇怪。 在测试过程中,我注意到现在速度快多了。 LINQ查询现在大约需要3秒钟才能处理20000行数据,这还算可以接受。更令人困惑的是: 我们的生产服务器上出现了相同的情况。 一小时前它非常慢,现在又变得很快了。 由于我是在开发服务器上进行测试的,所以我没有在生产服务器上做任何更改。我唯一能想到的问题就是两台服务器都是虚拟化的,并且与许多其他服务器共享SAN。我该如何找出是否是这个问题呢? - Dave

2

在怪罪LINQ之前,先找出实际延迟发生的地方。

  • 发送查询
  • 执行查询
  • 接收结果
  • 将结果转换为本地类型
  • 绑定/显示UI中的结果
  • 以及与此过程相关的任何其他事件

然后开始责怪LINQ ;)


@Dave,使用SQL Profiler查看何时以及什么数据进出SQL Server。简化客户端并使用Stopwatch类计时步骤。 - Emond
@Dave,你希望我如何在这么少的信息下帮助你?你连接到了正确的数据库吗(你使用的是本地副本吗?)你是否监控了正确的事件? - Emond
好的,我找到了LINQ发送的查询。 SQL Profiler向我展示了与我的应用程序中的秒表相同的持续时间。 - Dave
@Dave,你能在你的问题中添加查询吗? - Emond
我在我的问题中添加了查询。 - Dave
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接