2

新しいプロジェクトのためにいくつかの .NET データベース アクセス テクノロジを評価したところ、Entity Framework を使用してリモート データベースにクエリを実行すると、パフォーマンスが低下することがわかりました。Entity Framework は、LinqToSql または SqlClient よりも 10 倍遅くなりました。多分あなたは私がそれを説明したり修正したりするのを手伝ってくれるでしょうか?

テスト パラメータ:

データベース:

  • SQL Server 2008 エンタープライズ
  • 1000 レコードの 1 つのテーブル
  • テーブル構造:

    [dbo].[Master](
           [Id] [int] IDENTITY(1,1) NOT NULL,
           [Value_Bit] [bit] NOT NULL,
           [Value_Float] [float] NOT NULL,
           [Value_DateTime] [datetime2](7) NOT NULL,
           [Value_Uniqueidentifier] [uniqueidentifier] NOT NULL,
           [Value_NVarchar100] [nvarchar](100) NOT NULL,
           [Value_NVarchar1000] [nvarchar](1000) NOT NULL,
           [InsertDate] [datetime] NOT NULL,
           [UpdateDate] [datetime] NOT NULL,
           [Version] [timestamp] NOT NULL)
    

ベンチマーク アプリケーション:

  • .NET Framework 4 および 4.5
  • WinForms-Application としてホストされる

データベース アクセス技術:

  • エンティティ フレームワーク 5.0 (RC) および 4.3.1
  • LinqToSQL
  • SqlClient

コンピュータ (クライアント/サーバー):

  • 同様のハードウェアを備えた 3 台の異なるコンピューター
  • A と B が同じサブネットにある (例: 192.168.1.1 と 192.168.1.2)
  • C は A および B とは異なるサブネットにあります (例: 192.168.2.1)

select * from Masterクライアントまたはサーバーとして異なるコンピューター上で、各データベース アクセス テクノロジを使用して SQL クエリを直接実行しました。平均時間は 1000 回の反復の結果です。

テスト シナリオ 1:

  • クライアント:あ
  • サーバー: A

  • Entity Framework: 平均時間: 17 ミリ秒

  • LinqToSQL: 平均時間: 20 ミリ秒
  • SqlClient: 平均時間: 15 ミリ秒

テスト シナリオ 2:

  • クライアント:あ
  • サーバー: B

  • Entity Framework: 平均時間: 144 ミリ秒

  • LinqToSQL: 平均時間: 141 ミリ秒
  • SqlClient: 平均時間: 140 ミリ秒

テスト シナリオ 3:

  • クライアント:あ
  • サーバー: C

  • Entity Framework: 平均時間: 2145 ミリ秒

  • LinqToSQL: 平均時間: 151 ミリ秒
  • SqlClient: 平均時間: 156 ミリ秒

テスト シナリオ 4:

  • クライアント:B
  • サーバー: C

  • Entity Framework: 平均時間: 2060 ミリ秒

  • LinqToSQL: 平均時間: 141 ミリ秒
  • SqlClient: 平均時間: 178 ミリ秒

テスト シナリオ 3 と 4 の Entity Framework が LinqToSQL または SqlClient よりも 10 倍遅いのはなぜですか?

Entity Framework 4.3.1、5 (RC) および .NET Framework 4 および 4.5 でテストしましたが、毎回同じ結果でした。遅延読み込みと追跡を無効にし、コンパイル済みクエリとビューの事前生成を使用しましたが、違いはありませんでした。

実行された SQL クエリを SQL プロファイラで調査したところ、SQL Server で Entity Framework のクエリに既に 2 秒かかっていることがわかりました (テスト シナリオ 3)。コンピューター A の Management Studio からクエリを実行すると、100 ミリ秒しかかかりませんでした。

dotTrace (http://www.jetbrains.com) を使用してベンチマーク アプリケーションのプロファイルを作成したところ、ほとんどの実行時間がメソッドによって消費されていることがわかりましたToList。コール スタックをさらに詳しく調べると、メソッドが表示System.Data.SqlClient.SqlDataReader.GetString(Int32)され、最後SNINativeMethodWrapper.SNIReadSyncOverAsync(SafeHandle, IntPtr&, Int32)に常に消費されます。LinqToSql も SqlClient を使用しており、コール スタックはほぼ同じですが、実行時間は 10 倍高速です。

ボンネットの下で何が起こっているのかわかりません。コンピューター名の解決に関係している可能性がありますが、IP アドレスとそのコンピューター名を介してコンピューター C に ping を実行できます。それを説明したり、実行を高速化する方法をアドバイスしたりできる人はいますか?

前もって感謝します

マティアス

4

2 に答える 2

3

コードを見なければ、情報に基づいて推測することは困難ですが、一般的に見ることができる EF の典型的なことがいくつかあります。

  • 多くの場合、 Compiled Queriesを使用すると劇的に高速化できます。

  • EF クエリの遅延実行は、コードを見ると明らかではないトラップになる可能性があります。

よく見られる間違いは、IEnumerable または IQueryable コレクションを返すクエリを実行してから、これをループで使用することです。

// Execution will be deferred:
IEnumerable<person> peopleList = objectContext.People.Where(item => item.ID > 100);
foreach (person somePerson in peopleList)
{
  // do something here
}

このコードはデータベースに対して非常に多くの往復を行うため、重大なパフォーマンスの問題が発生する可能性があります。遅延実行と遅延ロードにより、ピープル リスト内の各項目のコードがデータベースに再度クエリを実行することになります。これがネットワーク経由で転送されるデータの量によっては、これだけでもパフォーマンスに深刻なダメージを与える可能性があります。

コレクションで ToList() メソッドを呼び出すだけで、このオーバーヘッドを削減できます。これにより、結果として得られるすべてのオブジェクトが 1 回のラウンドトリップでフェッチされます。

// Execution will be deferred:
List<person> peopleList = objectContext.People.Where(item => item.ID > 100)
                                              .ToList(); // Fetch objects NOW!

MSDN は、いくつかの提案を含む記事を提供しています。Performance Considerations (Entity Framework) :

パフォーマンス を向上させるための戦略 次の戦略を使用して、Entity Framework でのクエリの全体的なパフォーマンスを向上させることができます。

ビューの事前生成

エンティティ モデルに基づいてビューを生成することは、アプリケーションが初めてクエリを実行するときにかなりのコストがかかります。EdmGen.exe ユーティリティを使用して、設計中にプロジェクトに追加できる Visual Basic または C# コード ファイルとしてビューを事前に生成します。Text Template Transformation Toolkit を使用して、コンパイル済みのビューを生成することもできます。事前生成されたビューは実行時に検証され、指定されたエンティティ モデルの現在のバージョンと一貫性があることが保証されます。For more information, see How to: Pre-Generate Views to Improvement Query Performance (Entity Framework) and Isolating Performance with Precompiled/Pre- generated Views in the Entity Framework 4. 非常に大きなモデルを操作する場合は、次の考慮事項が適用されます。 NET メタデータ形式では、特定のバイナリ内のユーザー文字列の文字数が 16 に制限されています。777,215 (0xFFFFFF)。非常に大きなモデルのビューを生成していて、ビュー ファイルがこのサイズ制限に達すると、「さらにユーザー文字列を作成するための論理スペースが残っていません」というメッセージが表示されます。コンパイルエラー。このサイズ制限は、すべてのマネージド バイナリに適用されます。詳細については、大規模で複雑なモデルを操作する際のエラーを回避する方法を示すブログを参照してください。

クエリに NoTracking マージ オプションを使用することを検討してください

オブジェクト コンテキストで返されたオブジェクトを追跡するには、コストがかかります。オブジェクトへの変更を検出し、同じ論理エンティティに対する複数の要求が同じオブジェクト インスタンスを返すようにするには、オブジェクトを ObjectContext インスタンスにアタッチする必要があります。オブジェクトを更新または削除する予定がなく、ID 管理も必要ない場合は、クエリを実行するときに NoTracking マージ オプションを使用することを検討してください。

正しい量のデータを返す

一部のシナリオでは、Include メソッドを使用してクエリ パスを指定すると、必要なデータベースへのラウンド トリップが少なくなるため、はるかに高速になります。ただし、他のシナリオでは、関連オブジェクトをロードするためにデータベースへの追加のラウンドトリップが高速になる場合があります。これは、結合が少ない単純なクエリによってデータの冗長性が低下するためです。このため、関連オブジェクトを取得するさまざまな方法のパフォーマンスをテストすることをお勧めします。詳細については、「クエリ結果の整形 (Entity Framework)」を参照してください。1 つのクエリで返されるデータが多すぎないようにするには、クエリの結果をより管理しやすいグループにページングすることを検討してください。詳細については、「方法: クエリ結果をページングする (Entity Framework)」を参照してください。

ObjectContext のスコープを制限する

ほとんどの場合、using ステートメント内で ObjectContext インスタンスを作成する必要があります (Visual Basic では Using…End Using)。これにより、コードがステートメント ブロックを終了するときに、オブジェクト コンテキストに関連付けられたリソースが自動的に破棄されるようになるため、パフォーマンスが向上します。ただし、オブジェクト コンテキストによって管理されるオブジェクトにコントロールがバインドされている場合は、バインドが必要であり、手動で破棄される限り、ObjectContext インスタンスを維持する必要があります。詳細については、「オブジェクト サービスでのリソースの管理 (Entity Framework)」を参照してください。

データベース接続を手動で開くことを検討してください

アプリケーションが一連のオブジェクト クエリを実行したり、SaveChanges を頻繁に呼び出して作成、更新、および削除操作をデータ ソースに保持したりする場合、Entity Framework はデータ ソースへの接続を継続的に開いたり閉じたりする必要があります。このような状況では、これらの操作の開始時に手動で接続を開き、操作が完了したら接続を閉じるか破棄することを検討してください。詳細については、「Entity Framework での接続とトランザクションの管理」を参照してください。

于 2012-09-11T12:09:56.570 に答える
2

それぞれの問題は、巨大な時間差が突然消えました。管理者がネットワーク上で何かを変更したと言われたので、テスト パラメーターで何も変更していないので、それと関係があると推測します。

残念ながら、私は彼らが何を変更したのか分かりません。原因がわからないので、かなり残念です。

ありがとうございました。

于 2012-09-11T11:44:00.890 に答える