web-dev-qa-db-ja.com

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

NET Frameworkバージョン4でEntity Framework Code-Firstを使用した非常に単純なクエリに関連する、非常に奇妙なパフォーマンスが見られます。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を実行するとほぼ瞬時に実行されます。 C#コードを変更してSqlQuery関数を使用すると、5〜10ミリ秒で実行されます。

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

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

77
Brian Sullivan

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

 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; }

    ...
}
79
Brian Sullivan

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のバグである可能性があります。

36
cryss

Fluentマッピングを使用している場合、構成の一部としてIsUnicode(false)を使用して同じ効果を得ることができます-

http://msdn.Microsoft.com/en-us/data/jj591617.aspx#1.9

http://msdn.Microsoft.com/en-us/library/gg696416%28v=vs.103%29.aspx

7
Matt

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

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

3

私もこの問題を抱えていました。私の場合、犯人はSQL-Server parameter sniffing でした。

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

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

2
Oskar Sjöberg

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

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

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

2
user2622095

次のトリックを使用して、クエリを高速化できます-

  1. コンテキストを取得する直前に_ctx.Configuration.ProxyCreationEnabled_をfalseに設定します。
  2. また、.Select(c => new {c.someproperty})は必要なデータのみをフェッチし、全体をフェッチしません。

これが役立ったら教えてください。

1
Rajesh Panda