web-dev-qa-db-ja.com

ログ配布をセカンダリサーバーに復元した後、最初のストアドプロシージャの実行が遅くなる

すべてのSSRSレポート生成をオフロードするために、スタンバイ/読み取り専用のセカンダリSQLサーバーへのログ配布を設定しました。
これは、以下によって課される制限内で正常に機能します。

  1. トランザクションログの復元中にユーザーを追い出す(複数のインスタンスを設定し、ラウンドロビンスケジュールを使用して最新のトランザクションログを復元することで、これを回避しました)
  2. スケジュールされたトランザクションログのバックアップ/復元ジョブで示される時間枠までにデータが古くなっています。

残念ながら、すべてのストアドプロシージャを初めて実行したときは、トランザクションログが復元された後、通常よりも完了するまでにかなり長い時間がかかります。その同じストアドプロシージャの後続のすべての実行は、予想される時間内に完了します。次に別のストアドプロシージャを実行すると、最初は低速で、後続のすべての実行は予想時間内に完了します。

参考までに、実行の違いは通常、最初の実行の〜01:00と比較して〜00:02です。

これは、サーバーの実行統計またはストアドプロシージャのパラメータースニッフィング/ストアド実行プランのいずれかに関係があると思います。
この問題を回避する方法はありますか?または、これはトランザクションログの復元に固有のものですか?

ストアドプロシージャを初めて実行しただけの場合は、復元時にストアドプロシージャを実行することで簡単に回避できますが、すべてのストアドプロシージャを初めて実行したときに影響があるようです。

タッチのテストに使用しているストアドプロシージャを11個のテーブルでcount( * )を実行してみました。最初の実行には00:32、後続のcount(*)には00:00がかかりました。残念ながら、これはストアドプロシージャの最初の実行に影響を与えませんでした。

ストアドプロシージャの実行前でも実行後でも、プライマリサーバーまたはセカンダリサーバーでis_temporary統計の結果が表示されません。

現在SQL Server 2012を使用しています


クエリ実行計画:
一見するとクエリ実行プランは大きく異なりますが、実行プランを保存して生成された.sqlplanファイルを開くと、まったく同じです。違いは、私が使用しているSSMSのバージョンが異なるためで、2014年はプライマリサーバーで、2018年はセカンダリサーバーで発生しているようです。セカンダリで実行プランを表示すると、すべてのノードの%と時間コスト###の###(##%)の下に表示されます。これらの数値も実際の実行プランも、以降の実行で変化しません。
クライアントの統計情報も含めましたが、ほぼまったく同じです。唯一の違いは、プライマリサーバーがサーバー応答の待機時間1.4秒で実行され、セカンダリサーバーが81.3秒かかることです。

予想通り、最初の実行から多数のPAGEIOLATCH_SHロックが表示されます。

diff after first exec vs diff after second exec
waiting_tasks_count    10903    918  
wait_time_ms          411129  12768  

この状況の奇妙なことの1つは、セットアップのラウンドロビンの複数インスタンスの一部を除いて、定期的なトランザクションログによって供給され、発生しないスタンバイ/読み取り専用データベースから読み取りを行う本番SSRSサーバーが既にあることです。これらは、ストアドプロシージャの最初の実行時にスローダウンします。ただし、トランザクションログが復元されるたびにユーザーが開始されます。これは、上記の設定で解決されるはずの問題です。

7
RIanGillis

ここではいくつかの可能性のあることが起こりますが、これは完全ではないリストです:

  • 実行プランのキャッシュはログの復元によってクリアされるため、プランを最初に再コンパイルする必要があります。計画のコンパイル時間が長い場合、これは違いを説明することができます。最初の実行での遅延が後続の実行と比較してどれだけ長いかは正確には述べていません
    • これは最も可能性が低いようです-実際の実行プランXMLでプランのコンパイル時間を確認できます
  • バッファプールも復元中にクリアされるため、最初の実行時にすべてのデータをディスクから読み取る必要があります
    • これが事実である場合、おそらく待機統計をチェックすると、最初の実行中に高い _PAGEIOLATCH*_ 待機が表示されます。

これを軽減するためにできることがいくつかあります

  • バッファキャッシュを「ウォームアップ」します(SELECT COUNT(*) FROM dbo.YourTableを使用して重要なテーブルからメモリにすべてのデータを読み込むことにより)、
  • 復元後の手順としてすべての重要なストアドプロシージャを実行して、Procキャッシュを「ウォームアップ」します。

実行計画の「高速」と「低速」の例を提供すると、発生していることを正確に追跡するのに役立ちます。


SQL Server 2012以降を使用している場合は、同期統計の更新が原因で遅延が発生している可能性があります。ログ配布のセカンダリは読み取り専用であるため、これらの「読み取り可能なセカンダリ統計」はTempDBで作成されます。詳細については、こちらをご覧ください(記事はAGについてですが、このシナリオでも同じことが当てはまります)。

AlwaysOn:最新の統計を読み取り可能なセカンダリ、読み取り専用データベースおよびデータベーススナップショットで利用可能にする

これがスローダウンの原因となっている問題である場合、1つの解決策は、それらの統計を見つけて、それを本番データベースに作成し、復元後に最新で利用できるようにすることです。このクエリで一時的な統計を探すことができます:

_SELECT * FROM sys.stats WHERE is_temporary = 1;
_

指定した待機統計と、プランが同じであるという事実に基づいて、ログリストアによってバッファプールがクリアされるため、これはかなりの結論になります。

通常の実行では、12,768ミリ秒(ほぼ13秒)のIO待機が発生します。
最初の実行では、411,129 ms(ほぼ7)IO待機)を取得します。

実際のプロシージャとSELECT COUNT(*)クエリでは異なるインデックスが使用されているため、試したCOUNT(*)アプローチは役に立たなかった可能性があります。ここにはいくつかのオプションがあります。

  1. 各実行プランを調べ、使用されているインデックスを書き留めてから、復元後の手順としてこれらのインデックスをメモリにプルします。今回はインデックスヒントを使用します(SELECT COUNT(*) FROM dbo.YourTable WITH (INDEX (IX_Index_Being_Used_By_Proc))
  2. 各手順を復元後のステップとして実行するプロセスをスクリプト化するプロセスを実行します(これはオプション1よりも少し簡単に思えます)
  3. 多くの読み取りを実行する必要がないようにクエリを調整します(これがどれほど実行可能かわかりません)。
  4. I/Oサブシステムを高速化-より高速なディスク、ローカルSSD、SANへのチャネルの増加などを実現します(これはおそらく最も困難で最も高価なオプションです)
8
Josh Darnell