web-dev-qa-db-ja.com

Microsoft SQL Serverでのランダムな異常な不良実行プラン

こんにちは一緒に私は問題が何度か議論されたことを知っていますが、私たちは今、新しいSQL Server 2014 SP3で非常識な奇妙な問題に直面しており、私たちは遅れを取ることができません。

昨年、OracleからMS SQL Serverに切り替えました。私たちの市長DBはかなり大きいです。 800GB、大きなテーブル、PDMシステム。 +1000人のアクティブユーザー。 16コア、192 GBメモリ、SSD SANストレージ。ESX6.5

SQL Serverの設定:

  • 自動統計の作成を有効化
  • アドホッククエリ用に最適化= true
  • スナップショット分離が有効
  • 最大平行= 4
  • しきい値50
  • TempDBでの統計更新の非同期。通常、メインのDBで有効になっています。

とにかく、極端に悪いものとして扱われるクエリがあります。 SQlサーバーオプティマイザーが実行プランを作成し、それは大丈夫だと考えるが、実行時に予想される1〜2行ではなくMillionsとの内部結合(または複数)を実行するという事実につながります。そして、何百万もの論理的な読み取りを実行します。そして、私はこれで進行中のことを後回しにすることはできません。その後、これらのステートメントは数分で実行されました。

つまり、基本的に、すでに3つのデータベースがあります。すべて同じバージョンとハードウェア、製品テストと開発環境。私のテストはかなり簡単に行うことができます。すべてのデータベースは同じように構成され、同じ動作を示しますが、クエリは異なります。テスト環境が47秒間に同じステートメントを複数回実行するとします。proddbは1秒で完了します。他の製品は、製品版で数秒かかり、テスト版で理解します。一体何が起こっているのですか?私は常にステートメントを複数回再試行して、そのキャッシュが確実に行われるようにします。

クエリは、使用している4層アプリケーションによって生成されます。

1秒vs 47秒。これらのDBは両方とも同じSQLインスタンスを共有します。奇妙なことに、プランキャッシュをリセットした場合も、すぐに実行される可能性があります。しかし、どうにかして、オプティマイザでの計算の完全な混乱によって再び遅くなります。

ここで何が間違っていたのでしょうか?単一実行プランを修正する方法は? SQLサーバーがその極度に悪いステートメントから学習し、次に実行するときにそれを修正することができるのはなぜですか?


標準のトレースフラグを使用してテストDBを切り替えて再起動したため、互換モードはありません。

詳細なDB設定は次のとおりです: enter image description here

最初、クエリは10分間実行されます...最初の実行。今では即座にキャッシュされます。

https://www.brentozar.com/pastetheplan/?id=Syhb_Vvm4

DBCC TRACEOFF(9481)
SELECT  DISTINCT t_08.puid 
FROM PWORKSPACEOBJECT t_06 
, PRELEASE_STATUS_LIST t_07 
, PITEMREVISION t_08 
, PRELEASESTATUS t_02 
, PE9_MANUFACTURERSTORAGE t_09 
, PEN_ITEMREVMASTER t_01 
, PFORM t_05 
, PIMANRELATION t_03 
, PIMANRELATION t_04 
WHERE ( (  UPPER(t_06.pobject_type)  IN  ( UPPER( 'EN_Item Revision' ) ,  UPPER( 'EN_Item Revision' )  ) 
AND ( ( ( t_07.pvalu_0 = t_02.puid ) 
AND  UPPER(t_02.pname)  IN  ( UPPER( 'V' ) ,  UPPER( 'E9_D' ) ,  UPPER( 'F' ) ,  UPPER( 'E9_to_F' ) ,  UPPER( 'E9_M' )  ) ) 
AND ( ( ( t_08.puid = t_03.rprimary_objectu ) 
AND ( ( t_03.rrelation_typeu = 'gMx8h03uVJFL2B' ) 
AND ( ( t_03.rsecondary_objectu = t_05.puid ) 
AND ( ( t_05.rdata_fileu = t_01.puid ) 
AND ( ( t_01.re9_manufactureru = t_09.puid ) 
AND  ( UPPER(t_09.pe9_id)  =  UPPER( '7300007' )  ) ) ) ) ) ) 
AND ( ( t_08.puid = t_04.rprimary_objectu ) 
AND ( ( t_04.rrelation_typeu = 'gMx8h03uVJFL2B' ) 
AND ( ( t_04.rsecondary_objectu = t_05.puid ) 
AND ( ( t_05.rdata_fileu = t_01.puid ) 
AND  ( UPPER(t_01.phersteller_artikelnr)  =  UPPER( '00100893' )  ) ) ) ) ) ) ) ) AND ( t_06.puid = t_07.puid AND t_07.puid = t_08.puid ) );

しかし、それは今と同じはずです。

DBに対する最初のクエリが最初は速くなり、後で奇妙な実行計画のように50倍遅くなるとしたらどうでしょうか。


テスト環境の標準CEを有効にして、必要なプランキャッシュのワイプを行うと、上記のステートメントは、「適切なプラン」を使用してテスト環境で即座に実行されます。

「テスト」での古いCEでのテストを含むこのトピック全体は、本番環境で最も可能性の高い必要なインデックスを1つ作成すると、上記の悪いものと同様に悪い実行プランが作成されるという事実により生じました、3分以内に100%のCPU負荷を引き起こし、MS SQLのエキスパートが良い気分でそのインデックスを作成したとしても、現在、勤務時間中にすべてのセッションを3回強制終了する必要がありました。

不足しているインデックスステートメントは次のとおりです。

WITH cte_00000000059A53B0_17 AS (
         SELECT t_03.puid AS revPuid
               ,t_04.pdate_released AS revDateRel
               ,t_03.ritems_tagu AS myItem
               ,t_04.puid AS rlsPuid
               ,t_04.pname AS rlsName
         FROM   PWORKSPACEOBJECT t_01
               ,PRELEASE_STATUS_LIST t_02
               ,PITEMREVISION t_03
               ,PRELEASESTATUS t_04
               ,#PRefbindtag3
         WHERE  (
                    (
                        (
                            (
                                (
                                    t_03.ritems_tagu = #PRefbindtag3.puid
                                    AND (t_04.pdate_released <= @P1)
                                )
                                AND (t_04.pname = @P2)
                            )
                            AND (t_02.pvalu_0 = t_04.puid)
                        )
                        AND (t_01.pactive_seq != 0)
                    )
                    AND (t_01.puid = t_02.puid AND t_02.puid = t_03.puid)
                )
     )

SELECT DISTINCT t_07.myItem
      ,t_07.revPuid
      ,t_06.rDate
      ,t_07.rlsPuid
FROM   cte_00000000059A53B0_17 t_07
      ,(
           SELECT MAX(t_05.revDateRel) AS rDate
                 ,t_05.myItem AS dtItem
           FROM   cte_00000000059A53B0_17 t_05
           GROUP BY
                  t_05.myItem
       ) t_06
WHERE  (
           (
               (t_07.myItem = t_06.dtItem)
               AND (t_07.revDateRel = t_06.rDate)
           )
           AND (t_07.rlsName = @P3)
       )
ORDER BY
       t_06.rDate DESC

https://www.brentozar.com/pastetheplan/?id=rJJhK4dmN

USE [TCEUP01]
GO
CREATE NONCLUSTERED INDEX [<Name of Missing Index, sysname,>]
ON [dbo].[PRELEASESTATUS] ([pname],[pdate_released])
INCLUDE ([puid])
GO

このインデックスが有効になっているテスト環境で奇妙な計画が見当たらないため、週末に完全なDB再起動で同じことを試す必要があります。統計は良い点です。それらがすべて更新されたかどうかを確認します。これについて何かアイデアはありますか?


編集:

悲しいことに、私たちはまだ解決策を持っているようには見えません。先週私たちが調べたもの:

->一部の日付/時刻列がOracleから正しく移行されず、時間と分のタイムスタンプがないため、すべて日付と0:00になっていることがわかりました。本番環境での一般的な高負荷により、影響を受けるテーブルが修正されなくなりました。日付がよりアトミックになると、クエリが計算するデータがはるかに少なくなると思います。

-> prod dbをスタックさせた強く推奨されたインデックス作成が、10%未満のDb CPU負荷で問題なく作成されるようになることはかなり楽観的でした。

->昨日は勤務時間後に作成したので、DBへの負荷が少なくなります。数分以内に、アクティブなCTEステートメントが数十億、真剣に数十億の論理読み取りを持ち、まったく憤慨することはありませんでした。常に4つの同じプロセスIDであるようですが、前述のように、一部のコアを100%に増やし、実行を続けます。実行プランが新しいため、実行プランのキャッシュ全体をフラッシュすることはしませんでした。

->アクティブなステートメントを取得して、その実行プランを保存することしかできませんでした。悲しいことに、これは実際の実行計画ではなく、予期されたものでなければなりませんか?

https://www.brentozar.com/pastetheplan/?id=SkWRKSFN4

https://www.brentozar.com/pastetheplan/?id=ryCJ9BtNE

新しいインデックスの名前:EN_PIPRELEASESTATUS_1

とにかく、このインデックスは、同じテスト環境で問題なく実行されているのに、製品データベースを多額の計画で動かなくなっています。

アイデア?ありがとう

ところで、私たちはインディーを落とすことができました

BEGIN TRANSACTION

  -- lock table "a" till end of transaction
  SELECT top 1 puid
  FROM PRELEASESTATUS
  WITH (TABLOCK, HOLDLOCK)
  -- do some other stuff (including inserting/updating table "a")
  DROP INDEX [EN_PIPRELEASESTATUS_1] ON [dbo].[PRELEASESTATUS]
  -- release lock
  COMMIT TRANSACTION

GO

クレイジーになったいくつかのセッションのスクリーンショットを撮りました、それは最初のことでした enter image description here

1
Krautmaster

データベース設定が異なります。

最初の計画は、_compatibility level_ = 120のデータベースで作成されます。

_CardinalityEstimationModelVersion="120"
_

そして、2番目の計画は_legacy cardinality estimator_を使用して作成されました。

_CardinalityEstimationModelVersion="70"
_

したがって、ここで説明するように、2番目のデータベースでデータベース_compatibility level_を_120_に変更するか(すべてのクエリに影響します)、クエリヒント_(QUERYTRACEON 2312)_(このクエリにのみ影響します)を使用できます。 特定のクエリレベルで異なるトレースフラグによって制御できる、プランに影響を与えるSQL Serverクエリオプティマイザーの動作を有効にする

トレースフラグ2312は、クエリプランを作成するときに、クエリオプティマイザーがバージョン120(SQL Server 2014バージョン)のカーディナリティエスティメーターを使用するように強制します。

しかし、クエリで私が目にする主な問題は、_Oracle approach_を使用して記述したことです。

データベースが大文字と小文字を区別する照合を使用しない限り、UPPER()を使用しないでください。

たとえば、UPPER(t_06.pobject_type)を単純な_t_06.pobject_type_として書き換える必要があります。フィールドに関数を適用すると、このフィールドのインデックスを正常に除外して使用できなくなります。

_SQL Server_はNOT Oracleであり、大文字と小文字を区別する照合はほとんど使用されません。

2番目の観測は、クエリ内の結合の数に関するものです。

5つを超えるテーブルがある場合、オプティマイザは短い時間で十分なプランを見つける機会がないため、プランの1つで見つけた_StatementOptmEarlyAbortReason="TimeOut"_の理由で最適化を停止できます。

クエリを2つに分割し、最初のテーブルを最小のテーブル(フィルタリング後は最小)から開始し、_PE9_MANUFACTURERSTORAGE t_09_の場合は4つ以下のテーブルを結合して、結果を一時テーブルに保存します。次に、この一時テーブルを使用して他のテーブルを結合します。

[〜#〜]更新[〜#〜]

あなたが回答として投稿した最後の写真で_db collation_ IS _CASE SENSITIVE_。なぜ_binary collation_を選んだのか分かりません、それが最速です照合順序ですが、大文字と小文字を区別せずにUPPER()を使用したい場合、インデックスは使用されません。反対に、UPPER()を使用した場合、これは望ましくないようです大文字と小文字を区別するため、なぜbinaryを選択したのですか?

3
sepupic