SQL Server 2016の不適切なクエリプランにより、1週間に1回DBがロックされる


16

1週間に1度、過去5週間、ほぼ同じ時刻(早朝、人々が使用し始めたときのユーザーアクティビティに基づく場合があります)、SQL Server 2016(AWS RDS、ミラーリング)は多くのタイムアウトを開始しますクエリ。

すべてのテーブルの統計を更新すると、常にすぐに修正されます。

初回以降、すべてのテーブルのすべての統計を(毎週ではなく)毎晩更新しましたが、それでも起こりました(更新統計が実行されてから約8時間後ですが、毎日実行されるわけではありません)。

前回、クエリストアを有効にして、どの特定のクエリ/クエリプランであるかを確認できるかどうかを確認しました。私はそれを1つに絞り込むことができたと思います:

不正なクエリプラン

そのクエリを見つけた後、この頻繁に使用されないクエリから欠落している推奨インデックスを追加しました(ただし、頻繁に使用される多くのテーブルに影響します)。

不適切なクエリプランは、インデックススキャンを実行していました(1万行のみのテーブルで)。同じスキャンを実行するために使用されたミリ秒単位で返された他のクエリプラン。新しいインデックスを作成した後の最新のクエリプランは、シークのみを行います。しかし、そのインデックスがなくても、99%の時間で数ミリ秒以内に戻りましたが、毎週、40秒以上かかりました。

これは、2012年からSQL Server 2016に移行した後に発生し始めました。

DBCC CHECKDBはエラーを返しません。

  1. 新しいインデックスは問題を修正し、再び悪い計画を二度と選択しないようにしますか?
  2. うまく機能する計画を「強制」する必要がありますか?
  3. これが別のクエリ/プランで発生しないことを確認するにはどうすればよいですか?
  4. これはより大きな問題の症状ですか?

追加したばかりのインデックス:

CREATE NONCLUSTERED INDEX idx_AppointmetnAttendee_AttendeeType
ON [dbo].[AppointmentAttendee] ([UserID],[AttendeeType])

CREATE NONCLUSTERED INDEX [idx_appointment_start] ON [dbo].[Appointment]
(
    [ProjectID] ASC,
    [Start] ASC
)
INCLUDE (   [ID],
    [AllDay],
    [End],
    [Location],
    [Notes],
    [Title],
    [CreatedByID]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

完全なクエリテキスト:

https://pastebin.com/Z5szPBfu(LINQ生成、選択した列を最適化することができます/する必要がありますが、この問題とは無関係です)


タイムアウトしなかった以前の計画のスキャンが、同じサイズの別のテーブルで行われていることに気付きました。予定:11931行、予定の参加者:11937行。
プロのサウンディング名

回答:


16

あなたが質問した順序とは異なる順序で質問に答えます。

4.これはより大きな問題の症状ですか?

SQL Server 2016 の新しいカーディナリティー推定量は、この問題に寄与している可能性があります。SQL Server 2012はレガシーCEを使用しており、そのバージョンでは問題は発生していません。新しいカーディナリティ推定器は、データについて異なる仮定を行い、同じSQLに対して異なるクエリプランを生成できます。クエリとデータに応じて、レガシーCEで一部のクエリのパフォーマンスが向上する場合があります。そのため、データモデルの一部は、新しいCEに最適ではない場合があります。大丈夫ですが、今のところ新しいCEを回避する必要があるかもしれません。

また、毎日の統計情報の更新でも、一貫性のないクエリパフォーマンスが懸念されます。注意すべき重要な点の1つは、すべてのテーブルの統計を収集すると、キャッシュからすべてのクエリプランが効果的に消去されるため、統計に問題があるか、パラメータースニッフィングに関係する可能性があることです。データモデル、データ変更率、統計更新ポリシー、コードの呼び出し方法などに関する多くの情報がなければ決定を下すことは困難です。SQLServer 2016では、パラメータスニッフィングに役立つデータベースレベルの設定が提供されます。 、しかしそれは問題のあるクエリ1つだけではなく、アプリケーション全体に影響を与える可能性があります。

この動作につながる可能性のあるシナリオ例を紹介します。あなたが言った:

一部のユーザーは1つの許可レコードを持つことができ、一部は最大20kまで可能です。

すべてのクエリプランを一掃するすべてのテーブルの統計を収集するとします。上記の要因に応じて、その日の最初のクエリが1つのアクセス許可レコードのみを持つユーザーに対するものである場合、SQL Serverは、1つのレコードを持つユーザーには適切に機能するが、2万のレコードを持つユーザーにはひどく機能するプランをキャッシュします。その日の最初のクエリが2万件のレコードを持つユーザーに対するものである場合、2万件のレコードに対する適切な計画が得られる可能性があります。1レコードのユーザーに対してコードを実行すると、最適なクエリではない可能性がありますが、msで終了する可能性があります。パラメータスニッフィングのように聞こえます。問題が常に表示されない理由、または表示されるまでに数時間かかる理由を説明します。

1.新しいインデックスは問題を修正し、再び悪い計画を選択することはありませんか?

インデックスを介して必要なデータにアクセスする方が、特にスキャンが早期に終了できない場合、テーブルに対してクラスター化インデックススキャンを実行するよりも安価になるため、追加したインデックスの1つが問題を防ぐと思います。クエリプランの悪い部分を拡大してみましょう。

不適切なクエリプラン

SQL Serverは、1行のみに参加から返されると推定し[Permission][Project]。外部入力の各行に対して、クラスタ化インデックススキャンを実行し[Appointment]ます。このテーブルからすべての行がスキャンされますが、フィルタリングに一致する行のみ[Start]が結合演算子に返されます。結合演算子内では、結果はさらに削減されます。

結合の外部入力に送信される行が実際に1行しかない場合は、上記のクエリプランで問題ありません。ただし、結合からのカーディナリティの推定が間違っていて、たとえば1000行を取得する場合、SQL Serverは1000のクラスター化インデックススキャンを実行します[Appointment]ます。クエリプランのパフォーマンスは、推定の問題に非常に敏感です。

そのクエリプランを再度取得しない最も直接的な方法は、[Appointment]テーブルに対してカバーインデックスを作成することです。上のインデックスのようなもの[ProjectId][Start]、それを行う必要があります。これは、まさにこの[idx_appointment_start]問題に対処するために作成したインデックスのようです。SQLサーバーがクエリプランを選択しないようにする別の方法は、結合on [Permission]およびからカーディナリティの推定値を修正すること[Project]です。コードを変更する、統計を更新する、レガシーCEを使用する、複数列の統計を作成する、SQL Serverにローカル変数に関する詳細情報を与えるなどの典型的な方法RECOMPILEヒント、またはそれらの行を一時テーブルに具体化ます。これらの手法の多くは、msレベルの応答時間を必要とする場合、またはORMを介してコードを記述する必要がある場合、適切なアプローチではありません。

作成したインデックス[AppointmentAttendee]は、問題に直接対処する方法ではありません。ただし、インデックスの複数列の統計情報を取得するため、これらの統計情報が不適切なクエリプランを妨げる可能性があります。インデックスはデータにアクセスするためのより効率的な方法を提供する可能性があり、これも悪いクエリプランを阻止する可能性がありますが、インデックスがオンの場合にのみ再び発生しないという保証はないと思います[AppointmentAttendee]

3.これが別のクエリ/プランで発生しないことを確認するにはどうすればよいですか?

なぜあなたがこの質問をしているのか理解していますが、それは非常に広範な質問です。私の唯一のアドバイスは、クエリプランの不安定性の根本原因をよりよく理解し、ワークロードに適切なインデックスが作成されていることを検証し、ワークロードを慎重にテストおよび監視することです。Microsoftには、SQL Server 2016の新しいCEによって引き起こされるクエリプランの回帰に対処する方法に関する一般的なアドバイスがあります。

クエリプロセッサを最新バージョンのコードにアップグレードするための推奨ワークフローは次のとおりです。

  1. データベースの互換性レベルを変更せずにデータベースをSQL Server 2016にアップグレードします(以前のレベルに維持します)

  2. データベースでクエリストアを有効にします。クエリストアの有効化と使用の詳細については、「クエリストアを使用したパフォーマンスの監視」を参照してください。

  3. ワークロードの代表的なデータを収集するのに十分な時間待機します。

  4. データベースの互換性レベルを130に変更します

  5. SQL Server Management Studioを使用して、互換性レベルの変更後に特定のクエリでパフォーマンスの低下があるかどうかを評価します

  6. 回帰がある場合は、クエリストアで前の計画を強制します。

  7. 強制できないクエリプランがある場合、またはパフォーマンスがまだ不十分な場合は、互換性レベルを以前の設定に戻し、Microsoftカスタマーサポートに連絡することを検討してください。

SQL Server 2012にダウングレードして最初からやり直す必要があると言っているわけではありませんが、説明されている一般的な手法は役に立つかもしれません。

2.現在うまく機能している計画を「強制」する必要がありますか?

それは完全にあなた次第です。考えられるすべての入力パラメーターに対して適切に機能するクエリプランがあり、クエリストアの機能に満足しており、クエリプランの強制に伴う安心を求めている場合は、それを選択します。結局、リグレッションがあったクエリプランを強制することは、MicrosoftがSQL Server 2016へのアップグレードポリシーを推奨することの一部です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.