17

以下の executeTime は、初回は 30 秒、次に同じコード セットを実行するときは 25 秒です。SQL Profiler で見ていると、すぐにログインが表示され、そこに約 30 秒間留まります。次に、select ステートメントが実行されるとすぐに、アプリは ToList コマンドを終了します。Management Studio から生成されたクエリを実行すると、データベース クエリは約 400 ミリ秒しかかかりません。14 行と 350 列を返します。データベースの結果をエンティティに変換するのにかかる時間は非常に短いため、目立たないようです。

では、データベース呼び出しが行われる前の 30 秒間に何が起こっているのでしょうか?

エンティティ フレームワークがこれほど遅い場合、使用することはできません。私が間違っていること、またはこれを劇的にスピードアップするために変更できることはありますか?

更新: コンパイル済みクエリを使用すると、最初は 30 秒かかり、2 回目は 1/4 秒かかります。最初の通話をスピードアップするためにできることはありますか?

using (EntitiesContext context = new EntitiesContext()) 
{ 
    Stopwatch sw = new Stopwatch(); 
    sw.Start(); 
    var groupQuery = (from g in context.Groups.Include("DealContract") 
                    .Include("DealContract.Contracts") 
                    .Include("DealContract.Contracts.AdvertiserAccountType1") 
                    .Include("DealContract.Contracts.ContractItemDetails") 
                    .Include("DealContract.Contracts.Brands") 
                    .Include("DealContract.Contracts.Agencies") 
                    .Include("DealContract.Contracts.AdvertiserAccountType2") 
                    .Include("DealContract.Contracts.ContractProductLinks.Products") 
                    .Include("DealContract.Contracts.ContractPersonnelLinks") 
                    .Include("DealContract.Contracts.ContractSpotOrderTypes") 
                    .Include("DealContract.Contracts.Advertisers") 
                where g.GroupKey == 6 
                select g).OfType<Deal>(); 
    sw.Stop(); 
    var queryTime = sw.Elapsed; 
    sw.Reset(); 
    sw.Start(); 
    var groups = groupQuery.ToList(); 
    sw.Stop(); 
    var executeTime = sw.Elapsed; 
} 
4

4 に答える 4

15

これとまったく同じ問題があり、クエリに 40 秒かかっていました。

問題は.Include("table_name")機能にあることがわかりました。これらが多ければ多いほど、それは悪化しました。代わりに、クエリの直後に必要なすべてのデータを遅延ロードするようにコードを変更しました。これにより、合計時間が 40 秒から約 1.5 秒に短縮されました。私の知る限り、これはまったく同じことを達成します。

したがって、コードの場合は次のようになります。

var groupQuery = (from g in context.Groups
            where g.GroupKey == 6 
            select g).OfType<Deal>(); 

var groups = groupQuery.ToList();

foreach (var g in groups)
{
    // Assuming Dealcontract is an Object, not a Collection of Objects
    g.DealContractReference.Load();
    if (g.DealContract != null)
    {
        foreach (var d in g.DealContract)
        {
            // If the Reference is to a collection, you can just to a Straight ".Load"
            //  if it is an object, you call ".Load" on the refence instead like with "g.DealContractReference" above
            d.Contracts.Load();
            foreach (var c in d.Contracts)
            {
                c.AdvertiserAccountType1Reference.Load();
                // etc....
            }
        }
    }
}

ちなみに、現在のコードのクエリの上にこのコード行を追加すると、時間が約 4 ~ 5 秒短縮されます (私のオプションではまだ長すぎます) MergeOption.NoTracking。データベースを更新して挿入するための追跡オーバーヘッド:

context.groups.MergeOption = MergeOption.NoTracking;
于 2009-03-28T23:11:04.187 に答える
2

私が知っているクエリの初期コンパイルを高速化する唯一の方法は、クエリを単純化することです。Entity Frameworkコンパイル済みクエリのパフォーマンスに関する考慮事項に関する MSDN ドキュメントには、別のアプリケーション実行セッションで使用するためにコンパイル済みクエリを保存する方法があることは示されていません。

インクルードの数が多いと、インクルードの数が少なく、後で関連するエンティティに対してより多くのロードを実行する場合よりも、クエリの実行が遅くなることがわかったことを付け加えておきます。適切なメディアを見つけるには、試行錯誤が必要です。

ただし、ここに含めているすべてのエンティティのすべてのプロパティが本当に必要かどうかを尋ねなければなりません。このクエリには多数の異なるエンティティ タイプがあるように思われるため、それらを具体化すると非常にコストがかかる可能性があります。更新するつもりのない表形式の結果を取得しようとしているだけの場合、実際に必要な (比較的) 少数のフィールドをフラットな匿名型に射影すると、さまざまな理由で大幅に高速になります。また、これにより、熱心な読み込み、Load/IsLoaded の呼び出しなどについて心配する必要がなくなります。

エンティティ ビューをプリコンパイルすることで、最初のビューの生成を確実に高速化できます。これについては、MSDN にドキュメントがあります。ただし、最初のクエリが実行されたときにそのコストを支払うため、単純なクエリを使用したテストでは、これが 2 秒程度で実行されていることが示されます。2 秒というのはいいことですが、それ以外の節約にはなりません。

于 2009-03-26T20:57:12.590 に答える