web-dev-qa-db-ja.com

ストアドプロシージャはWebから呼び出されると遅くなり、Management Studioから速く呼び出される

Webアプリケーションから呼び出されるたびに非常にタイムアウトするプロシージャを保存しました。

Sql Profilerを起動し、タイムアウトする呼び出しを追跡し、最終的にこれらのことを見つけました。

  1. MS SQL Management Studio内から同じ引数を使用してステートメントを実行すると(実際、sqlプロファイルトレースからプロシージャコールをコピーして実行しました):平均で5〜6秒で終了します。
  2. しかし、Webアプリケーションから呼び出された場合、(トレースで)30秒以上かかるため、私のWebページは実際にはタイムアウトします。

私のウェブアプリケーションには独自のユーザーがいるという事実は別として、すべてのものは同じです(同じデータベース、接続、サーバーなど)、ウェブアプリケーションのユーザーを使ってスタジオで直接クエリを実行してみました。秒.

何が起こっているのかを知るにはどうすればよいですか?

トレースが実際の手順に遅延があることを明確に示しているため、BLL> DALレイヤーまたはテーブルアダプターを使用するという事実とは関係ないと仮定しています。それは私が考えることができるすべてです。

[〜#〜] edit [〜#〜]このリンク でADO.NETがARITHABORTをtrueに設定-これはほとんどの場合に適していますが、これが発生する場合があり、推奨される回避策はwith recompileオプションをストアドプロシージャに追加することです。私の場合、機能していませんが、これと非常によく似ていると思います。 ADO.NETが他に何をしているのか、どこで仕様を見つけることができるのか誰でも知っていますか?

92
iamserious

過去に同様の問題が発生したことがあるので、この質問の解決策を見たいと思っています。 Aaron BertrandのOPに関するコメントは Webから実行するとクエリはタイムアウトしますが、SSMSから実行すると非常に高速になります であり、質問が重複していない間、答えはあなたの状況に非常によく当てはまるかもしれません。

本質的に、SQL Serverにはキャッシュされた実行計画が破損しているように思えます。 Webサーバーで悪い計画にぶつかっていますが、ARITHHABORTフラグに別の設定があるため、SSMSは別の計画に到達します(そうしないと、特定のクエリ/ストアドプロシージャに影響しません)。

T-SQLストアドプロシージャを呼び出すとSqlTimeoutExceptionが発生するADO.NET を参照してください。別の例については、より完全な説明と解決策があります。

74

また、クエリがWebからゆっくり実行され、SSMSで高速に実行されることも経験しました。最終的に、問題はパラメータースニッフィングと呼ばれるものであることがわかりました。

私にとっての修正は、sprocで使用されるすべてのパラメーターをローカル変数に変更することでした。

例えば。変化する:

ALTER PROCEDURE [dbo].[sproc] 
    @param1 int,
AS
SELECT * FROM Table WHERE ID = @param1 

に:

ALTER PROCEDURE [dbo].[sproc] 
    @param1 int,
AS
DECLARE @param1a int
SET @param1a = @param1
SELECT * FROM Table WHERE ID = @param1a

奇妙に思えますが、それは私の問題を修正しました。

45
Zane

スパムではなく、他のユーザーにとって有益な解決策として、システムで高度なタイムアウトが発生しました。

sp_recompileを使用してストアドプロシージャを再コンパイルするように設定してみたところ、1つのSPの問題が解決しました。

最終的に、タイムアウトが発生したSPの数が多くなりましたが、その多くはこれまで一度も実行したことがなかったもので、DBCC DROPCLEANBUFFERSおよびDBBC FREEPROCCACHEを使用することにより、タイムアウトの発生率が大幅に低下しました。 、計画の再生成に時間がかかっていると思われるものや、SPのパフォーマンスが本当に不十分で、再評価が必要なものもあります。

7
Clint

WebアプリケーションがSPがトランザクションを開いたままにする前に、他のDB呼び出しが行われた可能性がありますか?それは、このSPが待機する理由である可能性がありますWebアプリケーションによって呼び出された場合、Webアプリケーションでの以前のアクションがこの問題を引き起こしていることを確認するために、Webアプリケーションで呼び出しを分離する(新しいページに置く)と言います。

4
Tundey

単純にストアドプロシージャ(私の場合はテーブル関数)を再コンパイルするとうまくいきました

1
Guy Biber

@Zaneが言ったように、パラメータスニッフィングが原因である可能性があります。私は同じ動作を経験し、プロシージャの実行計画と行のspのすべてのステートメントを調べました(すべてのステートメントをプロシージャからコピーし、パラメータを変数として宣言し、変数に同じ値を割り当てました)パラメータが持っていた)。ただし、実行計画はまったく異なって見えました。 spの実行には3〜4秒かかり、まったく同じ値を持つ行のステートメントが即座に返されました。

executionplan

いくつかのグーグル検索の後、私はその振る舞いについて興味深い読み物を見つけました: アプリケーションで遅い、SSMSで速い?

プロシージャをコンパイルするとき、SQL Serverは@fromdateの値が変化することを認識しませんが、@ fromdateの値がNULLであるという仮定の下でプロシージャをコンパイルします。 NULLを使用したすべての比較ではUNKNOWNが生成されるため、実行時に@fromdateにまだこの値がある場合、クエリは行をまったく返すことができません。 SQL Serverが入力値を最終的な真理としてとる場合、テーブルにまったくアクセスしない定数スキャンのみでプランを構築できます(クエリの実行* SELECT FROM FROM Orders WHERE OrderDate> NULLの例を参照) 。ただし、SQL Serverは、実行時に@fromdateの値に関係なく、正しい結果を返すプランを生成する必要があります。一方、すべての価値に最適な計画を立てる義務はありません。したがって、行は返されないという前提があるため、SQL Serverはインデックスシークに対応します。

問題は、nullのままにできるパラメータがあり、nullとして渡された場合、デフォルト値で初期化されることでした。

create procedure dbo.procedure
    @dateTo datetime = null
begin
    if (@dateTo is null)
    begin
        select @dateTo  = GETUTCDATE()
    end

    select foo
    from dbo.table
    where createdDate < @dateTo
end

に変更した後

create procedure dbo.procedure
    @dateTo datetime = null
begin
    declare @to datetime = coalesce(@dateTo, getutcdate())

    select foo
    from dbo.table
    where createdDate < @to
end 

再び魅力のように機能しました。

1
TomPez