5

現在、私はかなり複雑なデータベースに取り組んでいます。オブジェクト モデルは、データベースにマップされるように設計されています。手動で生成された POCO クラスで EF 5 を使用しています。

すべてが機能していますが、パフォーマンスについて不満があります。EF でパフォーマンスの問題が発生したことは一度もないので、今回はひどく間違ったことをしただけなのか、それとも問題が別の場所にあるのか疑問に思っています。

メインクエリは、動的パラメータで構成されている場合があります。概念的には次のような if ブロックと switch ブロックがいくつかあります。

if (parameter != null) { query = query.Where(c => c.Field == parameter); }

また、いくつかの複雑な And/Or の組み合わせについては、Albahari の LinqKit 拡張機能を使用しています。

クエリは、年と年の​​データを含む「注文」の大きなテーブルに対して行われます。ただし、平均的な使用は2か月の範囲フィルターです。

メイン クエリが作成されるとSkip/Take、Take が 10 要素に設定された組み合わせでページ分割されます。

このすべての後、IQueryable はレイヤーを介して送信され、Automapper が使用される MVC レイヤーに到達します。

ここで、Automapper が反復を開始する (したがって、クエリが実際に実行される) と、独自のナビゲーション プロパティなどを持つ一連のナビゲーション プロパティが呼び出されます。含めるエンティティが 3 つまたは 4 つを超える場合は、積極的な読み込みを避けるために、すべてが EF の推奨事項に従って遅延読み込みに設定されます。私のシナリオは次のようなものです:

  • 注文 (最大 10)
    • Order の下の多くのナビゲーション プロパティ
      • これらの一部には、その下に他のナビゲーションがあります (ローカリゼーション エンティティ)
    • 注文の詳細 (注文ごとに多くの注文の詳細)
      • 各注文詳細の下にある多数のナビゲーション プロパティ
        • これらの一部には、その下に他のナビゲーションがあります (ローカリゼーション エンティティ)

これにより、1 つのレンダリングされた「ページ」に対して合計 300 以上のクエリが簡単に発生します。これらの各クエリは非常に高速で、数ミリ秒で実行されますが、それでも 2 つの主な懸念事項があります。

  • 遅延ロードされたプロパティは順番に呼び出され、並列化されないため、より多くの時間がかかります
  • 前のポイントの結果として、データベースはクエリごとに sql を受け取り、実行し、返す必要があるため、各クエリの間にデッドタイムがあります。

それがどのように行われたかを確認するために、熱心な読み込みで同じクエリを作成しようとしましたが、予想どおり、翻訳された SQL は 7,000 行 (はい、7,000 行) を超え、全体的にはるかに遅くなりました。

このシナリオでは、EF と Linq が適切な選択ではないとは考えたくありません。必要なデータをすべてフェッチするストアド プロシージャを作成すると、数十倍速く実行されると言う人もいます。私はそれが真実であるとは信じていません.関連するすべてのエンティティの自動実体化を失うことになります.

次のように、改善するためにできることをいくつか考えました。

  • 選択した列を減らすためのテーブル分割
  • このシナリオは読み取り専用であるため (追跡されていないエンティティがあるため)、オブジェクトの追跡をオフにします。

これらすべてを踏まえると、主な不満は、結果ページ (MVC 4 で実行) のレンダリングが遅すぎることです。少し診断した後、「ネットワーク時間」ではなく「サーバー時間」のように見え、約 8 から 12 かかります。サーバー時間の秒。

私の経験から、これは起こるべきではありません。このクエリの必要性に間違った方法でアプローチしているのか、それとも他の何かに注意を向ける必要があるのか​​ 疑問に思っています(おそらく、設定が不適切なIISサーバー、または私が本当に無知な何か)。言うまでもなく、データベースのインデックスは正常であり、DBA によって非常に注意深くチェックされています。

したがって、誰かがこれについて見逃しているヒント、アドバイス、ベストプラクティスを持っている場合、またはこのシナリオで遅延読み込みを使用して EF を使用するのは完全に間違っていると言うことができます...大歓迎です。

4

4 に答える 4

4

大量の階層データを表示する非常に複雑なクエリの場合、ストアド プロシージャは通常、適切なアプローチをとれば、LINQ/EF よりもパフォーマンスの面で役に立ちません。お気づきのように、EF の 2 つの "すぐに使える" オプション (遅延読み込みと熱心な読み込み) は、このシナリオではうまく機能しません。ただし、これを最適化するための優れた方法がいくつかあります。

(1) 一連のエンティティをメモリに読み込んでオートマッパー経由でマッピングするのではなく、可能であればクエリで直接「自動マッピング」を行います。例えば:

var mapped = myOrdersQuery.Select(o => new OrderInfo { Order = o, DetailCount = o.Details.Count, ... })
    // by deferring the load until here, we can bring only the information we actually need 
    // into memory with a single query
    .ToList();

このアプローチは、複雑な階層内のフィールドのサブセットのみが必要な場合に非常にうまく機能します。また、階層データを選択する EF の機能により、フラットな表形式のデータよりも複雑なものを返す必要がある場合、ストアド プロシージャを使用するよりもはるかに簡単になります。

(2) 複数の LINQ クエリを手動で実行し、結果をメモリにアセンブルします。例えば:

// read with AsNoTracking() since we'll be manually setting associations
var myOrders = myOrdersQuery.AsNoTracking().ToList();
var orderIds = myOrders.Select(o => o.Id);
var myDetails = context.Details.Where(d => orderIds.Contains(d.OrderId)).ToLookup(d => d.OrderId);
// reassemble in memory
myOrders.ForEach(o => o.Details = myDetails[o.Id].ToList());

これは、すべてのデータが必要でありながら、できるだけ多くの EF マテリアライゼーションを利用したい場合に非常にうまく機能します。ほとんどの場合、ストアド プロシージャ アプローチではこれ以上のことはできませんが (生の SQL で動作するため、複数の表形式のクエリを実行する必要があります)、既に LINQ で記述したロジックを再利用することはできません。

(3) Include() を使用して、どのアソシエーションが熱心に読み込まれるかを手動で制御します。これを #2 と組み合わせて、一部のアソシエーションの EF 読み込みを利用しながら、他のアソシエーションを手動で読み込む柔軟性を提供できます。

于 2013-08-13T12:40:04.843 に答える
2

ビューのデータを取得するための効率的でシンプルな SQL クエリを考えてみてください。

それは可能ですか?

そうでない場合は、データを取得するために必要な結合が少なくなるように、テーブルを分解(非正規化) してみてください。また、データの取得を高速化するために、テーブルの列に効率的なインデックスはありますか?

はいの場合は、EF を忘れて、ストアド プロシージャを作成し、それを使用してデータを取得します。

選択したクエリの追跡をオフにすることは、読み取り専用のシナリオでは必須です。私の数字を見てください:

http://netpl.blogspot.com/2013/05/yet-another-orm-micro-benchmark-part-23_15.html

ご覧のとおり、追跡シナリオと非追跡シナリオの違いは重要です。

私は熱心な読み込みを試してみましたが、どこでもではなく (7k 行の長いクエリにならないように)、選択したサブクエリで試してみました。

于 2013-05-24T19:13:43.063 に答える
0

考慮すべき点として、EF は開発時間を大幅に短縮するのに確実に役立ちます。ただし、DB から大量のデータを返す場合、EF は動的 SQLを使用していることを覚えておく必要があります。これは、EF が 1. SQL を作成し、2.SQL Server が実行計画を作成する必要があることを意味します。これは、クエリが実行される前に発生します。

ストアド プロシージャを使用すると、SQL Server は実行プラン (パフォーマンスのために編集可能) をキャッシュできるため、EF を使用するよりも高速になります。しかし...いつでもストアドプロシージャを作成してEFから実行できます。ストアド プロシージャに変換してから EF から呼び出す複雑なプロシージャまたはクエリ。次に、パフォーマンスの向上を確認し、そこから再評価できます。

于 2013-05-24T19:12:41.340 に答える
0

場合によっては、Compiled Queries MSDNを使用して、クエリのパフォーマンスを大幅に向上させることができます。アイデアは、異なるパラメーターを使用して同じ SQL 呼び出しを生成する可能性がある、何度も実行される共通のクエリがある場合、最初に実行されたときにクエリ タイをコンパイルしてから、それをデリゲートとして渡すことで、Entity Framework の再呼び出しのオーバーヘッドを排除するというものです。後続の呼び出しごとに SQL を生成します。

于 2013-08-13T13:45:03.847 に答える