14

EF5 から EF6 にアップグレードしようとすると、null 許容列でテーブルを検索する際に重大なパフォーマンス ギャップが発生します。以下にサンプルを示します。

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 を指定すると、managerId = null のクエリは行を選択しません。お気遣いに感謝しますが、99.9% の検索ロジックが分離されています。1 つのユースケースでは がwhere ManagerId == null検索され、別のユースケースでは特定の ID が検索されます。where ManagerId == managerId

問題はパフォーマンスに大きな影響を与えることです。MS SQL は ManagerId のインデックスを使用せず、テーブル スキャンが発生します。私のプロジェクトには何百もの同様の検索があり、EF6 へのアップグレード後のデータベース サイズは約 100 GB の全体的なパフォーマンスで、約 10 分の 1 になりました。

問題は、EF6 でこの障害を無効にして単純な SQL を生成するための何らかの構成または規則を知っている人はいますか?

編集:

プロジェクトで同様の選択を12個チェックしたところ、次のことがわかりました。

  • 場合によっては、SQL SERVER は、検索するフィールドに指定されたインデックスを使用します。この場合でも、わずかなパフォーマンスの低下があります。インデックスを 2 回使用します。1 回目はパラメーターで指定した値を探し、2 回目は null を探します。
  • EF6 は、定数が null ではないと正確に指定されている場合でも、null をチェックします。次に例を示します。

                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)。2 番目の部分は常にfalse ですが、この部分を追加するとインデックスが失われます。

約 170 万件のレコードの定期的なインポート (通常は約 30 分かかります) は 3 時間で完了し、進行状況は約 30% です。

4

2 に答える 2

1

非常に異なる答え

varchar(xxx) を使用している場合、LNQ to SQL は nvarchar(4000) を吐き出します。これにより、インデックスと変換が中断され、SQL 計画が大幅に吹き飛ばされます。私の場合、奇妙なヌル動作のためにこの質問を見つけましたが、それは問題ではありませんでした。以下の回答は、null と nvarchar の問題の両方を解決します。SQL プランは ~11 から .006 になりました。

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

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

(はい、一般に公開された GUID を保存するなど、varchar を使用する理由はたくさんあります)

于 2016-07-26T23:59:13.450 に答える