6月27日、PASS Performance Virtual Chapterは、2013年夏のパフォーマンスPaloozaを開催しました。これは24時間のPASSの縮小版ですが、パフォーマンス関連のトピックのみに焦点を当てています。 「パフォーマンスを損なう可能性のある10の悪い習慣」というタイトルのセッションを行い、次の10の概念を扱いました。
- SELECT *
- ブラインドインデックス
- スキーマプレフィックスなし
- デフォルトのカーソルオプション
- sp_プレフィックス
- キャッシュの肥大化を許可する
- 幅広いデータ型
- SQLServerのデフォルト
- 機能の使いすぎ
- 「自分のマシンで動作します」
これらのトピックのいくつかは、私の「悪い習慣とベストプラクティス」の講演や、6月の初めから今週までKevinKlineと一緒に主催している毎週のクエリチューニングウェビナーなどのプレゼンテーションから覚えているかもしれません。 (ちなみに、これらの6本のビデオは8月上旬にYouTubeで利用できるようになります。)
私のセッションには351人の参加者があり、素晴らしいフィードバックを得ることができました。私はそのいくつかに取り組みたかった。
まず、構成の問題です。私は新しいマイクを使用していましたが、すべてのキーストロークが雷のように聞こえるだろうとは思いもしませんでした。周辺機器の配置を改善することでこの問題に対処しましたが、影響を受けたすべての人に謝罪したいと思います。
次に、ダウンロード。デッキとサンプルはイベントサイトに投稿されます。これらはページの下部にありますが、ここからダウンロードすることもできます。
最後に、セッション中に投稿された質問のリストを以下に示します。ライブの質疑応答で回答されなかったものに確実に対処したいと思いました。1か月足らずでこれを絞り込んだことをお詫びします。 、しかしありました たくさんの質問があり、それらを部分的に公開したくありませんでした。
Q:指定されたパラメーターに対して入力値が大幅に変化する可能性のあるprocがあり、その結果、キャッシュされたプランがほとんどの場合に最適ではない場合は、proc WITH RECOMPILEを作成し、小さいものを使用するのが最適です。実行するたびにパフォーマンスが低下しますか?
A: これは実際にはさまざまな要因(計画の複雑さを含む)に依存するため、ケースバイケースでこれに取り組む必要があります。また、モジュール全体ではなく、影響を受けるステートメントのみがヒットするように、ステートメントレベルの再コンパイルを実行できることにも注意してください。 Paul Whiteは、多くの場合、RECOMPILE
を使用してパラメータスニッフィングを「修正」することを思い出しました。 、しかしあまりにも多くの場合、それは2000スタイルのWITH RECOMPILE
を意味します はるかに優れたOPTION (RECOMPILE)
ではなく 、これはステートメントに限定されるだけでなく、パラメータの埋め込みも可能にします。これはWITH RECOMPILE
ではない。したがって、RECOMPILE
を使用する場合 パラメータスニッフィングを阻止するには、モジュールではなく、ステートメントに追加します。
A: 上記のように、これは計画のコストと複雑さに依存し、「はい、常に大きなパフォーマンスの打撃があります」と言う方法はありません。また、それを別の方法と比較する必要があります。
Q:insertdateにクラスター化されたインデックスがある場合、後でデータを取得するときに変換関数を使用します。直接比較を使用する場合、クエリの日付は読み取れません。現実の世界では、何がより良い選択ですか。A: 「実世界で読める」とはどういう意味かわかりません。特定の形式で出力する場合は、通常、クライアント側で文字列に変換することをお勧めします。 C#およびプレゼンテーション層で使用する可能性のある他のほとんどの言語は、データベースからの日付/時刻出力を任意の地域形式にフォーマットする能力を超えています。
Q:キャッシュされたプランが使用される回数をどのように決定しますか?その値を持つ列、またはこの値を与えるインターネット上のクエリはありますか?最後に、そのようなカウントは最後の再起動以降にのみ関連しますか?A: ほとんどのDMVは、最後のサービス開始以降にのみ有効であり、他のDMVでさえ、より頻繁にフラッシュされる可能性があります(オンデマンドでも、不注意と故意の両方で)。もちろん、プランキャッシュは常に流動的であり、キャッシュからドロップアウトしたAFAIKプランは、元に戻った場合、以前のカウントを維持しません。したがって、キャッシュにプランが表示されても、私は100%ではありません。見つけた使用回数を信じることができると確信しています。
そうは言っても、おそらくあなたが探しているのはsys.dm_exec_cached_plans.usecounts
です。 また、sys.dm_exec_procedure_stats.execution_count
もあります。 プロシージャ内の個々のステートメントがキャッシュに見つからない場合のプロシージャの情報を補足するのに役立ちます。
A: これに関する主な懸念事項は、OUTER APPLY
などの特定の構文を使用できることです。 またはテーブル値関数を持つ変数。低い互換性を使用するとパフォーマンスに直接影響する場合はありませんが、通常推奨されるいくつかのことは、インデックスを再構築して統計を更新することです(そして、ベンダーに新しい互換性レベルをできるだけ早くサポートさせることです)。かなりの数のケースで予期しないパフォーマンスの低下を解決するのを見てきましたが、そうする必要はなく、おそらく賢明ではないという意見も聞いています。
A: いいえ、少なくともパフォーマンスに関しては、SELECT *
の例外が1つあります。 EXISTS
内で使用する場合は問題ではありません 句。しかし、なぜ*
を使用するのでしょうか。 ここ? EXISTS (SELECT 1 ...
–オプティマイザーはそれらを同じように扱いますが、ある意味でコードを自己文書化し、サブクエリがデータを返さないことを読者が確実に理解できるようにします(大きなEXISTS
を見逃した場合でも) 外側)。 NULL
を使用する人もいます 、なぜ1を使い始めたのかわかりませんが、NULL
が見つかりました 少し直感的でもありません。
*注意*EXISTS (SELECT *
を使用する場合は、注意が必要です。 スキーマにバインドされているモジュール内:
CREATE VIEW dbo.ThisWillNotWork WITH SCHEMABINDING AS SELECT BusinessEntityID FROM Person.Person AS p WHERE EXISTS (SELECT * FROM Sales.SalesOrderHeader AS h WHERE h.SalesPersonID = p.BusinessEntityID);
このエラーが発生します:
メッセージ1054、レベル15、状態6、プロシージャThisWillNotWork、6行目構文'*'は、スキーマにバインドされたオブジェクトでは許可されていません。
ただし、SELECT 1
に変更します うまく動作します。したがって、これはSELECT *
を回避するためのもう1つの引数かもしれません。 そのシナリオでも。
A: さまざまな言語にまたがっておそらく数百があります。命名規則と同様に、コーディング標準は非常に主観的なものです。どの規則が最適であるかは重要ではありません。 tbl
がお好きなら プレフィックス、ナッツに行きます! bigEndianよりもPascalを優先します。列名の前にintCustomerID
などのデータ型を付けたい 、私はあなたを止めるつもりはありません。より重要なことは、規則を定義し、それを*一貫して*使用することです。
とはいえ、私の意見が必要な場合は、私はそれらに不足はありません。
Q:XACT_ABORTはSQL Server 2008以降で使用できるものですか?
A: XACT_ABORT
を廃止する予定はありません したがって、引き続き正常に機能するはずです。率直に言って、TRY / CATCH
があるので、これがあまり頻繁に使用されることはありません。 (およびTHROW
SQL Server 2012以降)。
A: 私はこれをテストしませんでしたが、多くの場合、スカラー関数をインラインのテーブル値関数に置き換えると、パフォーマンスに大きな影響を与える可能性があります。私が見つけた問題は、この切り替えを行うと、APPLY
の前に作成されたシステムでかなりの量の作業が発生する可能性があることです。 存在していたか、このより良いアプローチを採用していない人々によって管理されています。
A: 2つのことが頭に浮かびます:(1)遅延はコンパイル時間に関係している、または(2)遅延はクエリを満たすためにロードされているデータの量に関係している、そしてそれが初めてディスクから来なければならないメモリーではありません。 (1)の場合、SQL Sentry Plan Explorerでクエリを実行でき、ステータスバーに最初と後続の呼び出しのコンパイル時間が表示されます(ただし、これには1分がかなり長すぎるように思われ、ありそうもないです)。違いが見つからない場合は、システムの性質である可能性があります。このクエリで、すでにバッファプールにある他のデータと組み合わせてロードしようとしているデータの量をサポートするにはメモリが不足しています。これらのいずれかが問題であると思わない場合は、2つの異なる実行が実際に異なる計画をもたらすかどうかを確認してください。違いがある場合は、answers.sqlperformance.comに計画を投稿してください。喜んで確認します。 。実際、Plan Explorerを使用して両方の実行の実際の計画をキャプチャすると、I / Oの違いについても通知され、SQLServerが最初の遅い実行に時間を費やしている可能性があります。
Q:sp_executesqlを使用してパラメータースニッフィングを取得しています。プランスタブのみがキャッシュにあるため、アドホックワークロード用に最適化するとこれを解決できますか?
A: いいえ、アドホックワークロードの最適化設定がこのシナリオに役立つとは思いません。パラメータースニッフィングは、同じプランの後続の実行が異なるパラメーターに使用され、パフォーマンス動作が大幅に異なることを意味するためです。アドホックワークロードの最適化は、多数の異なるSQLステートメントがある場合に発生する可能性のあるプランキャッシュへの大幅な影響を最小限に抑えるために使用されます。したがって、sp_executesql
に送信する多くの異なるステートメントのプランキャッシュへの影響について話しているのでない限り、 –これはパラメータスニッフィングとしては特徴付けられません– OPTION (RECOMPILE)
を試してみてください より良い結果が得られる可能性があります。または、さまざまなパラメータの組み合わせで良好な結果が得られるパラメータ値がわかっている場合は、OPTIMIZE FOR
を使用してください。 。ポールホワイトからのこの回答は、はるかに優れた洞察を提供する可能性があります。
A: もちろん、OPTION (RECOMPILE)
を含めるだけです。 動的SQLテキストの場合:
DBCC FREEPROCCACHE; USE AdventureWorks2012; GO SET NOCOUNT ON; GO EXEC sp_executesql N'SELECT TOP (1) * INTO #x FROM Sales.SalesOrderHeader;'; GO EXEC sp_executesql N'SELECT TOP (1) * INTO #x FROM Sales.SalesOrderDetail OPTION (RECOMPILE);' GO SELECT t.[text], p.usecounts FROM sys.dm_exec_cached_plans AS p CROSS APPLY sys.dm_exec_sql_text(p.[plan_handle]) AS t WHERE t.[text] LIKE N'%Sales.' + 'SalesOrder%';
結果:Sales.SalesOrderHeader
を示す1行 クエリ。
ここで、バッチ内のいずれかのステートメントにOPTION (RECOMPILE)
が含まれていない場合 、プランはまだキャッシュされている可能性があり、再利用することはできません。
A: ええと、BETWEEN
>= AND <
と意味的に同等ではありません 、むしろ>= AND <=
、およびまったく同じ方法で最適化および実行します。いずれにせよ、私は意図的にBETWEEN
を使用しません 日付範囲のクエリでは、これまでに、制限のない範囲にする方法がないためです。 BETWEEN
を使用 、両端が包括的であり、これは基になるデータ型によっては非常に問題になる可能性があります(現在または将来の変更により、知らない可能性があります)。タイトルは少し厳しいように思われるかもしれませんが、次のブログ投稿でこれについて詳しく説明します。
悪魔と悪魔の共通点は何ですか?
Q:カーソルで「localfast_forward」は実際に何をしますか?
A: FAST_FORWARD
実際にはREAD_ONLY
の短縮形です およびFORWARD_ONLY
。彼らがしていることは次のとおりです:
LOCAL
外部スコープ(デフォルトではカーソルはGLOBAL
)になるようにします インスタンスレベルのオプションを変更していない限り)。-
READ_ONLY
カーソルを直接更新できないようにします。例:WHERE CURRENT OF
を使用する 。 -
FORWARD_ONLY
スクロールする機能を防ぎます。FETCH PRIOR
を使用する またはFETCH ABSOLUTE
FETCH NEXT
の代わりに 。
私が示したように(そしてブログで説明したように)、これらのオプションを設定すると、パフォーマンスに大きな影響を与える可能性があります。この機能のセットから実際に逸脱する必要のあるカーソルが本番環境で表示されることはめったにありませんが、通常は、とにかくはるかに高価なデフォルトを受け入れるように作成されています。
Q:カーソルとwhileループのどちらがより効率的ですか?
A: WHILE
ループは、デフォルトのオプションを使用した同等のカーソルよりもおそらく効率的ですが、LOCAL FAST_FORWARD
を使用した場合、違いはほとんどないと思います。 。一般的に、WHILE
ループはカーソルと呼ばれることのないカーソルであり、私は昨年、私が間違っていることを証明するために、高く評価されている同僚に挑戦しました。彼らのWHILE
ループはそれほどうまくいきませんでした。
A: usp_
プレフィックス(またはsp_
以外のプレフィックス 、またはその問題のプレフィックスなし)は、私が示したのと同じ影響を*与えません*。ストアドプロシージャでプレフィックスを使用することにはほとんど価値がありません。EXEC something
というコードを見つけたときに疑いが生じることはめったにないからです。 、何かがストアドプロシージャであるため、そこにはほとんど価値がありません(たとえば、ビューをテーブルと区別するためにビューにプレフィックスを付けるのとは異なり、ビューは交換可能に使用できるため)。すべてのプロシージャに同じプレフィックスを付けると、たとえばObjectExplorerで目的のオブジェクトを見つけるのが非常に難しくなります。電話帳のすべての名前の前にLastName_
が付いていると想像してみてください。 –それはどのように役立ちますか?
A: はい! SQLServer2008以降を使用している場合。同一の2つのプランを特定した後も、それらには別々のplan_handle
があります。 値。したがって、保持したくないものを特定し、そのplan_handle
をコピーします。 、このDBCC
内に配置します コマンド:
DBCC FREEPROCCACHE(0x06.....);Q:procでif elseなどを使用すると悪い計画が発生しますか?それは最初の実行に対して最適化され、そのパスに対してのみ最適化されますか?では、各IFのコードのセクションを個別の手順にする必要がありますか?
A: SQL Serverはステートメントレベルの最適化を実行できるようになったため、これは、プロシージャ全体を1つのユニットとして再コンパイルする必要があった古いバージョンよりも大幅な効果はありません。
Q:spのパラメータースニッフィングの問題がなくなるため、動的SQLを作成する方がよい場合があります。これは本当ですか ?このシナリオに関して行うべきトレードオフまたはその他の考慮事項はありますか?A: はい、動的SQLは、特に大規模な「キッチンシンク」クエリに多くのオプションのパラメータがある場合に、パラメータのスニッフィングを妨げることがよくあります。上記の質問で他の考慮事項をいくつか扱いました。
Q:テーブルにDATEPART(mycolumn、year)として計算列があり、そのインデックスにある場合、SQLサーバーはこれをSEEKで使用しますか?A: すべきですが、もちろんそれはクエリに依存します。インデックスは、出力列をカバーしたり、他のフィルターを満たしたりするのに適していない可能性があり、使用するパラメーターは、シークを正当化するのに十分な選択性がない可能性があります。
Q:すべてのクエリに対して計画が生成されますか?些細なものでも計画は立てられますか?A: 私の知る限り、プランの生成を妨げるエラーがない限り、有効なクエリごとにプランが生成されます(これは、無効なヒントなど、複数のシナリオで発生する可能性があります)。それらがキャッシュされるかどうか(およびそれらがキャッシュにとどまる時間)は、他のさまざまな要因に依存します。その一部については、上記で説明しました。
Q:sp_executesqlを呼び出すと、キャッシュされたプランが生成(および再利用)されますか?
A: はい、まったく同じクエリテキストを送信する場合は、直接発行するか、sp_executesql
を介して送信するかは問題ではありません。 、SQLServerはプランをキャッシュして再利用します。
A: 理由がわかりません。私が抱える唯一の懸念は、ファイルの即時初期化では、開発者が多数の自動拡張イベントに気付かない可能性があることです。これは、本番環境に非常に異なる影響を与える可能性のある不十分な自動拡張設定を反映している可能性があります(特にこれらのサーバーのいずれかがそうでない場合) *IFIを有効にします。
Q:SELECT句の関数を使用して、コードを複製する方がよいと言うのは正しいでしょうか?
A: 個人的にはそうだと思います。 SELECT
のスカラー関数を置き換えることで、多くのパフォーマンスマイレージを獲得しました。 そのコードを繰り返さなければならない場合でも、インラインで同等のリストを作成します。ただし、前述のように、場合によっては、それをインラインのテーブル値関数に置き換えると、厄介なパフォーマンスの低下なしにコードを再利用できることがあります。
A: データスキューには要因があります。それは、生成/シミュレートしているデータの種類と、スキューからどれだけ離れているかによって異なると思います。たとえば、本番環境で通常90文字の長さのvarchar(100)列があり、データ生成で平均50(SQL Serverが想定するもの)のデータが生成される場合、数値への影響は大きく異なります。ページ数と最適化、そしておそらくあまり現実的ではないテスト。
しかし、正直に言うと、この特定の側面は、私が多くの時間を費やしたものではありません。なぜなら、私は通常、実際のデータを取得する方法をいじめることができるからです。 :-)
Q:クエリのパフォーマンスを調べるときに、作成されるすべての関数は同じですか?そうでない場合は、可能な場合は避けるべき既知の機能のリストがありますか?A: いいえ、すべての機能がパフォーマンスの点で同等に作成されているわけではありません。作成できる関数には3つの異なるタイプがあります(当面はCLR関数を無視します):
- マルチステートメントスカラー関数
- マルチステートメントテーブル値関数
- インラインテーブル値関数
インラインスカラー関数はドキュメントに記載されていますが、これらは神話であり、SQLServerの時点では少なくとも2014年は、サスカッチやネス湖の怪物と並んで言及されることもあります。
一般的に、できれば80ptフォントで入力しますが、インラインテーブル値関数は適切であり、他の関数は最適化がはるかに難しいため、可能な場合は避ける必要があります。
関数は、決定論的であるかどうか、スキーマにバインドされているかどうかなど、パフォーマンスに影響を与えるさまざまなプロパティを持つこともできます。
多くの機能パターンについては、パフォーマンスに関する考慮事項が必ずあります。また、それらに対処することを目的としたこのConnectアイテムにも注意する必要があります。
Q:カーソルなしで現在の合計を維持できますか?A: はい、できます。カーソル以外にもいくつかの方法があります(私のブログ投稿「累計のベストアプローチ– SQL Server 2012用に更新)で詳しく説明されています):
- SELECTリストのサブクエリ
- 再帰CTE
- 自己参加
- 「風変わりなアップデート」
- SQL Server 2012+のみ:SUM()OVER()(デフォルト/ RANGEを使用)
- SQL Server 2012以降のみ:SUM()OVER()(ROWSを使用)
SQL Server 2012を使用している場合は、最後のオプションが断然最善のアプローチです。そうでない場合は、カーソルをより魅力的な選択肢にする他の非カーソルオプションに制限があります。たとえば、風変わりな更新方法は文書化されておらず、期待した順序で機能することが保証されていません。再帰CTEでは、使用しているシーケンシャルメカニズムにギャップがないことが必要です。サブクエリと自己結合のアプローチは単純にスケーリングしません。