【发布时间】:2013-11-09 11:14:43
【问题描述】:
尝试从 EF5 升级到 EF6,我遇到了可空列搜索表的显着性能差距。这是一个示例:
public class Customer
{
public int Id { get; set; }
public int? ManagerId { get; set; }
//public virtual Manager Manager { get; set; }
}
public class MyContext : DbContext
{
public MyContext(string connstring): base(connstring){}
public DbSet<Customer> Customers { get; set; }
}
class Program
{
static void Main(string[] args)
{
var db = new MyContext("CONNSTRING");
var managerId = 1234;
var q = from b in db.Customers
where b.ManagerId == managerId
select b.Id;
var s = q.ToString();
}
}
EF6在生成SQL的时候,会增加一些空值处理的逻辑:
SELECT
[Extent1].[Id] AS [Id]
FROM [dbo].[Customers] AS [Extent1]
WHERE (([Extent1].[ManagerId] = @p__linq__0)
AND ( NOT ([Extent1].[ManagerId] IS NULL OR @p__linq__0 IS NULL)))
OR (([Extent1].[ManagerId] IS NULL) AND (@p__linq__0 IS NULL))
注意相同的 linq 在 EF5 下生成更简单的 SQL:
SELECT
[Extent1].[Id] AS [Id]
FROM [dbo].[Customers] AS [Extent1]
WHERE [Extent1].[ManagerId] = @p__linq__0
我可以理解开发人员试图实现的观点:如果您提供 null 作为参数,则 where managerId = null 的查询将不会选择任何行。感谢您的关心,但 99.9% 的搜索逻辑是分开的:一个用例查找 where ManagerId == null,另一个用例搜索特定 id where ManagerId == managerId
问题是对性能的影响很大:MS SQL 没有在 ManagerId 上使用索引,并且发生了表扫描。我的项目有数百个类似的搜索,数据库大小约为 100GB,升级到 EF6 后整体性能减少了大约 10。
问题是有人知道在 EF6 中禁用此障碍并生成简单 sql 的某种配置或约定吗?
编辑:
我在我的项目中检查了十几个类似的选择,发现:
- 在某些情况下,SQL SERVER 确实使用为字段 I 指定的索引 搜索。即使在这种情况下,也会有轻微的性能损失:它 使用索引两次:第一次寻找我在 参数,第二次找null
-
EF6 甚至在常量被准确指定为非空时检查空,例如:
from p in db.PtnActivations where p.Carrier != "ALLTEL" where p.Carrier != "ATT" where p.Carrier != "VERIZON"
生成 SQL
WHERE ( NOT (('ALLTEL' = [Extent1].[Carrier]) AND ([Extent1].[Carrier] IS NOT NULL))) AND ( NOT (('ATT' = [Extent1].[Carrier]) AND ([Extent1].[Carrier] IS NOT NULL))) AND ( NOT (('VERIZON' = [Extent1].[Carrier]) AND ([Extent1].[Carrier] IS NOT NULL)))
没有使用我在运营商上的索引。 EF5版本有
( NOT (('ALLTEL' = [Extent1].[Carrier]))) AND ( NOT (('ATT' = [Extent1].[Carrier]))) AND ( NOT (('VERIZON' = [Extent1].[Carrier]) ))
使用它。
注意条件('ALLTEL' = [Extent1].[Carrier]) AND ([Extent1].[Carrier] IS NOT NULL)。第二部分总是 false,但添加这部分会放弃索引。
我对大约 170 万条记录的例行导入(通常需要大约 30 分钟)已经持续了 3 个小时,进度约为 30%。
【问题讨论】:
-
我看不出两个 SQL 选择有什么不同。他们应该选择相同的行集。如果
ManagerId上没有索引,我看不出MySQL 在这两种情况下如何使用与表扫描不同的策略。您确定性能下降是由此造成的,而不是其他原因吗? -
wilx,上面的数据库模式是人为的,但模式很常见:一个表在另一个表上有可为空的外键(当然还有索引)。当 select 很简单时,MS SQL 确实使用索引,例如 where managerid = parameter 并且不要在 EF6 情况下使用它。我有每个 db 操作的性能时间的 sql 日志(使用 EF5 中的 EFTracingProvider 和 EF6 中的 DBCommandInterceptor 实现),我看到了确切的瓶颈。为了验证,我在 MSQL SMS 中运行了一个 sql 并检查了两种情况下的计划
标签: entity-framework entity-framework-6