为什么使用ToList()
强制实现会使我的查询数量级更快,如果有的话,它应该完全相反?
1)立即致电First()
// "Context" is an Entity Framework DB-first model
var query = from x in Context.Users
where x.Username.ToLower().Equals(User.Identity.Name.ToLower())
select x;
var User = query.First();
// ** The above takes 30+ seconds to run **
2)在致电First()
后致电ToList()
:
var query = from x in Context.Users
where x.Username.ToLower().Equals(User.Identity.Name.ToLower())
select x;
var User = query.ToList().First(); // Added ToList() before First()
// ** Now it takes < 1 second to run! **
获取生成的SQL后,唯一的区别是,正如预期的那样,在第一个查询中添加了TOP (1)
。正如Andyz Smith在下面的回答中所说,根本原因是SQL Server优化器在这种特殊情况下,在添加TOP (1)
时选择了更差的执行计划。因此,问题与LINQ(通过添加TOP (1)
做了正确的事情)以及与SQL Server的特性有关的所有事情都无关。
答案 0 :(得分:11)
我只能想到一个原因......
要测试它,您能否删除Where
子句并重新运行测试?如果结果是第一个语句更快,请在此处注释,我将解释原因。
修改
在LINQ语句Where子句中,您使用的是字符串的.ToLower()方法。我的猜测是LINQ没有为这个方法内置转换为SQL,所以生成的SQL是行
SELECT *
FROM Users
现在,我们知道LINQ延迟加载,但它也知道,因为它没有评估WHERE
子句,所以需要加载元素来进行比较。
<强>假设强>
第一个查询是延迟加载结果集中的 EVERY 元素。然后进行.ToLower()比较并返回第一个结果。这会导致对服务器的n
个请求以及巨大的性能开销。没有看到SQL Tracelog就无法确定。
第二个语句调用ToList,它在执行ToLower比较之前请求批处理SQL,导致只有一个服务器请求
替代假设
如果探查器仅显示一个服务器执行,请尝试使用Top 1子句执行相同的查询,并查看它是否需要相同的时间。根据这篇文章(Why is doing a top(1) on an indexed column in SQL Server slow?),TOP子句有时会弄乱SQL服务器优化器并使用正确的索引来阻止它。
好奇心编辑
尝试将LINQ更改为
var query = from x in Context.Users
where x.Username.Equals(User.Identity.Name, StringComparison.OrdinalIgnoreCase)
select x;
感谢@Scott找到在LINQ中进行不区分大小写的比较的方法。试一试,看看它是否更快。
答案 1 :(得分:3)
SQL与Linq lazy loading不同。因此,您对.ToList()
的调用将强制.Net评估表达式,然后在内存中选择first()
项。
其他选项应该将top 1
添加到SQL
E.G。
var query = from x in Context.Users
where x.Username.ToLower().Equals(User.Identity.Name.ToLower())
select x;
//SQL executed here
var User = query.First();
和
var query = from x in Context.Users
where x.Username.ToLower().Equals(User.Identity.Name.ToLower())
select x;
//SQL executed here!
var list = query.ToList();
var User = query.First();
如下所示,第一个查询应该更快!我建议做一个SQL profiler来看看发生了什么。查询的速度取决于您的数据结构,记录数,索引等。
您的测试时间也会改变结果。正如一些人在评论中提到的,第一次点击EF时,需要初始化并加载元数据。所以如果你一起运行这些,第一个应该总是很慢。
以下是有关EF performance considerations
的更多信息注意这一行:
加载实体框架使用的模型和映射元数据 元数据工作空间。此元数据全局缓存并可用 到同一应用程序域中的ObjectContext的其他实例。
&安培;
因为与数据库的开放连接消耗了宝贵的价值 资源,实体框架打开和关闭数据库 仅在需要时连接。你也可以明确地打开 连接。有关更多信息,请参阅管理连接和 实体框架中的交易。
答案 2 :(得分:0)
因此,优化器选择了一种运行查询的错误方法。
由于您无法向SQL添加优化程序提示以强制优化程序选择更好的计划,因此我看到了两个选项。
在检索/包含在选择中的所有列上添加覆盖索引/索引视图相当荒谬,但我认为它会起作用,因为该索引会让它变得容易为优化者选择更好的计划。
始终过早地实现包含First或Last或Take的查询。危险,因为随着数据变大,在本地拉取所有数据和执行First()并在服务器上使用Top进行查询之间的收支平衡点将会发生变化。
https://groups.google.com/forum/m/#!topic/microsoft.public.sqlserver.server/L2USxkyV1uw