Entity Frameworkのクエリは遅いが、SqlQueryの同じSQLは速い


93

Entity Framework Code-Firstと.NET Frameworkバージョン4を使用した非常に単純なクエリに関連する、非常に奇妙なパフォーマンスがいくつかあります。LINQ2Entitiesクエリは次のようになります。

 context.MyTables.Where(m => m.SomeStringProp == stringVar);

実行には3000ミリ秒以上かかります。生成されたSQLは非常にシンプルに見えます。

 SELECT [Extent1].[ID], [Extent1].[SomeStringProp], [Extent1].[SomeOtherProp],
 ...
 FROM [MyTable] as [Extent1]
 WHERE [Extent1].[SomeStringProp] = '1234567890'

このクエリは、Management Studioを通じて実行されると、ほぼ瞬時に実行されます。SqlQuery関数を使用するようにC#コードを変更すると、5〜10ミリ秒で実行されます。

 context.MyTables.SqlQuery("SELECT [Extent1].[ID] ... WHERE [Extent1].[SomeStringProp] = @param", stringVar);

したがって、まったく同じSQLであり、結果のエンティティはどちらの場合も変更が追跡されますが、2つの場合のパフォーマンスは大きく異なります。何ができますか?


2
初期化の遅延が発生していると思います-おそらくコンパイルを表示してください。MSDNを参照:Performance Considerations for Entity Framework 5
Nicholas Butler

ビューを事前に生成してみましたが、役に立たないようです。また、遅いものの前に別のEFクエリを実行して、初期化に関するものを除外しました。最初のクエリ中にコンテキストのウォームアップが発生した場合でも、新しいクエリはすばやく実行され、遅いクエリはまだゆっくり実行されていました。
Brian Sullivan

1
@marc_s-いいえ、SqlQueryは完全にマテリアライズされた変更追跡エンティティインスタンスを返します。msdn.microsoft.com/en-us/library/…を
ブライアンサリバン

EFクエリに対して生成されたSQLは、実際にパラメーター値をインライン化していますか、それともパラメーターを使用していますか?これは個々のクエリのクエリ速度には影響しませんが、時間の経過とともにサーバーでクエリプランの肥大化を引き起こす可能性があります。
ジムウーリー2013

同じクエリを2回または複数回実行してみましたか?2回目の実行にはどのくらい時間がかかりましたか?これを.NET Framework 4.5で試しましたか?.NET Framework 4.5にはいくつかのEF関連のパフォーマンス改善があり、役立つ可能性があります。
Pawel 2013

回答:


96

それを見つけた。SQLデータ型の問題であることがわかりました。SomeStringPropデータベースの列はvarcharでしたが、EFでは、.NET文字列型はnvarcharであると想定しています。DBが比較を行うためのクエリ中に結果として生じる変換プロセスは、長時間かかります。私はEF Profが私をここで少し誤解させていたと思います、実行されているクエリのより正確な表現は次のようになります:

 SELECT [Extent1].[ID], [Extent1].[SomeStringProp], [Extent1].[SomeOtherProp],
 ...
 FROM [MyTable] as [Extent1]
 WHERE [Extent1].[SomeStringProp] = N'1234567890'

したがって、結果として生じる修正は、コード優先モデルに注釈を付け、正しいSQLデータ型を示すことです。

public class MyTable
{
    ...

    [Column(TypeName="varchar")]
    public string SomeStringProp { get; set; }

    ...
}

1
素敵な調査。:それは、ここで説明するようにあなたのクエリは、「暗黙の型変換」に苦しんでいたbrentozar.com/archive/2012/07/...
ハイメ・

デバッグの数時間を節約できました。これはまさに問題でした。
コーディ

1
私の場合、varcharすべてに使用するレガシーデータベースでEDMXを使用していますが、これが問題でした。EDMXですべての文字列列のvarcharを考慮することができるかどうか疑問に思います。
Alisson 2017年

1
素晴らしい発見者。しかし、@ JaimeデータベースからEFモデルを更新した後、すべて(たとえば、dbモデルのデータアノテーション)が消去されるため、データベースの最初のアプローチで何をすべきか。
Nauman Khan

しばらくの間、これを私のホームページに設定することで、このような素晴らしい答えを見つけたときの興奮をしばらくの間思い出すことができます。ありがとうございました!!!
OJisBad

43

EFで作成したクエリを遅くする理由は、null許容スカラーとnull許容スカラーを比較することでした。

long? userId = 10; // nullable scalar

db.Table<Document>().Where(x => x.User.Id == userId).ToList() // or userId.Value
                                ^^^^^^^^^    ^^^^^^
                                Type: long   Type: long?

そのクエリには35秒かかりました。しかし、そのような小さなリファクタリング:

long? userId = 10;
long userIdValue = userId.Value; // I've done that only for the presentation pursposes

db.Table<Document>().Where(x => x.User.Id == userIdValue).ToList()
                                ^^^^^^^^^    ^^^^^^^^^^^
                                Type: long   Type: long

信じられないほどの結果をもたらします。完了するのにわずか50msかかりました。EFのバグである可能性があります。


13
これはとても奇妙です
ダニエルカルデナス

1
ああ、神様。これは、インターフェイスIUserId.Idを使用して問題が発生した場合にも発生する可能性がありますが、最初にIdを整数にマッピングすると機能します... 100.000行のアプリケーションですべてのクエリを確認する必要がありますか?
Dirk Boer

このバグは報告されていますか?まだ最新バージョン6.2.0です
Dirk Boer

2
同じ問題がEF Coreにもあります。これを見つけてくれてありがとう!
Yannickv

もう1つの提案は、LINQ式に入れる前に変数を処理することです。そうしないと、生成されたSQLははるかに長く、遅くなります。LINQ式の中にTrim()とToLower()があると、私を悩ませました。
samheihey


4

同じ問題がありましたが(SQLマネージャーから実行するとクエリが高速になります)、EFから実行するとタイムアウトになります。

エンティティ(ビューから作成されたもの)に誤ったエンティティキーがあることがわかりました。そのため、エンティティには同じキーを持つ重複行があり、背景でグループ化する必要があったと思います。


3

また、複雑なefクエリでこれに遭遇しました。6秒のefクエリを生成する1秒未満のsqlクエリに削減した1つの修正は、遅延読み込みをオフにすることでした。

この設定(ef 6)を見つけるには、.edmxファイルに移動し、[プロパティ]-> [コード生成]-> [遅延読み込み有効]を確認します。falseに設定します。

パフォーマンスが大幅に向上しました。


4
それはかっこいいですが、ポスターの質問とは関係ありません。
ジェイスレア2015

2

私にもこの問題がありました。私の場合の犯人はSQL-Server パラメータの盗聴だったことがわかりました

私の問題が実際にパラメータースニッフィングが原因であった最初の手がかりは、「set arithabort off」または「set arithabort on」でクエリを実行すると、Management Studioで実行時間が大幅に異なることでした。これは、ADO.NETではデフォルトで「set arithabort off」が使用され、Management Studioではデフォルトで「set arithabort on」が使用されるためです。クエリプランキャッシュは、このパラメーターに応じて異なるプランを保持します。

クエリのクエリプランのキャッシュを無効にしました。解決策はここにあります

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.