14

尝试从 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 时,会添加一些用于 null 处理的逻辑:

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,另一个用例搜索特定 idwhere ManagerId == managerId

问题是对性能的影响很大:MS SQL 不使用 ManagerId 上的索引并且发生表扫描。我的项目有数百个类似的搜索和数据库大小约 100GB 的整体性能在升级到 EF6 后减少了大约 10。

问题是有人知道某种配置或约定来禁用 EF6 中的这个障碍并生成简单的 sql 吗?

编辑:

我在我的项目中检查了十几个类似的选择,发现:

  • 在某些情况下,SQL SERVER 确实使用为我搜索的字段指定的索引。即使在这种情况下,也会有轻微的性能损失:它使用索引两次:第一次查找我在参数中指定的值,第二次查找 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)。第二部分总是错误的,但添加这部分会放弃索引。

我对大约 170 万条记录的例行导入(通常需要大约 30 分钟)已持续 3 小时,进度约为 30%。

4

2 回答 2

14

db.Configuration.UseDatabaseNullSemantics = true;

获得您在 EF5 中的行为。该工作项描述了 和 之间的区别,true并且false应该帮助您确定您是否可以接受旧行为。

于 2013-10-30T17:21:59.263 回答
1

非常不同的答案

如果您使用的是 varchar(xxx),则 LNQ to SQL 会输出 nvarchar(4000),这会破坏索引和转换,从而大大破坏您的 sql 计划。就我而言,由于奇怪的 null 行为,我发现了这个问题,但这不是问题。下面的答案解决了 null 和 nvarchar 问题。SQL 计划从 ~11 变为 0.006。

public class InterestingRow
{
    [Key]
    public int interesting_row_id { get; set; }

    [StringLength(255), Required, Column(TypeName = "varchar")]
    public string public_guid { get; set; }
}

(是的,使用 varchar 的原因有很多,例如您要存储公开的 guid)

于 2016-07-26T23:59:13.450 回答