6

SQL データベース クエリが突然(ただし定期的には約 3 週間ごとに) 遅くなるという問題があります。

セットアップは次のとおりです。

  • Windows Server 2008 (R2 以外) 64 ビット、8 GB RAM
  • SQL Server Express 2008 R2
  • データベースのサイズは 6 GB (mdf ファイル サイズ)
  • クエリが主に選択しているテーブル ( Orders) には約 24000 レコードがあり、他の 5 つの結合テーブルは小さい (100 レコード以下)
  • テーブルOrdersには、平均サイズが約 200 ~ 300 kB のバイナリ データ (PDF ドキュメント) を含むvarbinary(MAX)列があります (ただし、最大で 2 MB になる場合もあります)。Reportこれらの 24000 件の注文の 90% 以上がこの列に入力されており、その他の注文は入力されていますNULL。つまり、6 GB のデータベース サイズの 90% 以上がバイナリ データです。

問題のクエリの構造は次のとおりです。

SELECT TOP (30) [Project2].[OrderID] AS [OrderID]
                -- around 20 columns more
FROM ( SELECT [Project2].[OrderID] AS [OrderID],
              -- around 20 columns more
              row_number() OVER (ORDER BY [Project2].[OrderID] ASC) AS [row_number]
       FROM ( SELECT [Filter1].[OrderID] AS [OrderID]
              -- around 20 columns more
              FROM ( SELECT [Extent1].[OrderID] AS [OrderID]
                     -- around 20 columns more
                     FROM [dbo].[Orders] AS [Extent1]
                     INNER JOIN -- small table
                     LEFT OUTER JOIN  -- small table
                     LEFT OUTER JOIN  -- small table
                     LEFT OUTER JOIN  -- small table
                     LEFT OUTER JOIN  -- small table
                     WHERE ([Extent1].[Status] IS NOT NULL) 
                       AND (4 = CAST( [Extent1].[Status] AS int))
                       AND ([Extent1].[SomeDateTime] IS NULL)
                       AND ([Extent1].[Report] IS NULL)
                   ) AS [Filter1]
              OUTER APPLY  (SELECT TOP (1) [Project1].[C1] AS [C1]
                            FROM ( SELECT CAST( [Extent7].[CreationDateTime] AS datetime2) AS [C1],
                                                [Extent7].[CreationDateTime] AS [CreationDateTime]
                                   FROM [dbo].[OtherTable] AS [Extent7]
                                   WHERE [Filter1].[OrderID] = [Extent7].[OrderID]
                                 ) AS [Project1]
                             ORDER BY [Project1].[CreationDateTime] DESC
             ) AS [Limit1]
       )  AS [Project2]
)  AS [Project2]
WHERE [Project2].[row_number] > 0
ORDER BY [Project2].[OrderID] ASC

これは、Entity Framework による LINQ-to-Entities クエリから生成されます。WHEREクエリは、最初の句のみが異なるいくつかのバリエーションで発生します。

  • 5つのバリエーション

    WHERE ([Extent1].[Status] IS NOT NULL) 
      AND (X = CAST( [Extent1].[Status] AS int))
    

    0X は ~ の間で指定できます4。これらのクエリは決して問題ではありません。

  • と 2 つのバリアント (*)

    WHERE ([Extent1].[Status] IS NOT NULL) 
      AND (4 = CAST( [Extent1].[Status] AS int))
      AND ([Extent1].[SomeDateTime] IS NULL)
      AND ([Extent1].[Report] IS NULL)
    

    または... IS NOT NULL...最後の行に。以下に説明する問題は、これら 2 つのクエリでのみ発生します。

「現象」は次のとおりです。

  • 2 つのクエリ (*) は、1 日あたり 100 ~ 200 回、週 5 日実行されます。約 3 週間、1 秒未満で動作します。
  • 3 週間後、両方のクエリが突然 60 秒以上必要になりました。(この時間は、データベースのサイズが大きくなるにつれて実際には長くなります。) タイムアウトにより、ユーザーは (Web ページ上で、つまり Web アプリで) エラーを受け取ります。(Entity Framework は、デフォルトで結果を 30 秒以上待機しないようです。)
  • クエリを SSMS に貼り付けてクエリを実行すると (60 秒待機)、結果が正常に返され、次の同じクエリが 1 秒以内に再度実行されます。
  • 約 3 週間後、同じことが再び起こります (ただし、クエリの実行時間は 65 秒または 70 秒になります)。

追加の観察:

  • クエリが正常に実行されているときに SQL Server サービス プロセスを再起動すると、プロセスのメモリ使用量がゆっくりと増加します。約 1 週間以内に、段階的に約 1.5 GB (タスク マネージャーのプライベート ワーキング セット) の制限に達します。
  • クエリが突然遅くなったときに SQL Server サービス プロセスを再起動し、クエリを再度トリガーすると、サービスが数秒以内にほぼ 1 GB をロードすることがタスク マネージャーで確認できます。

どういうわけか、問題全体が Express エディションと列のメモリ制限 (1 GB) に関係していると思われますが、列の値が であるかどうかをチェックする句で使用varbinary(MAX)しているだけです。列自体は、選択された列の 1 つではありません。WHERENULLNULLReport

私は来年最新の Express エディションの制限 (10 GB の mdf ファイル サイズ) に対して実行しているので、とにかく変更を検討しています。

  • バイナリ列を別のテーブルに移動し、FILESTREAM を介してコンテンツを外部に保存し、Express Edition を使用し続ける
  • Express の制限なしで「大きな」SQL Server エディションのいずれかを使用し、バイナリ列をOrdersテーブルに保持します
  • 両方を行う

質問: クエリが突然遅くなった理由は何ですか? 私が計画している変更の 1 つは問題を解決できますか、それとも他の解決策はありますか?

編集

以下のコメントにある bhamby のヒントに従ってSET STATISTICS TIME ON、クエリを再度実行する前に SSMS で設定しました。クエリが再び遅くなるとSQL Server parse and compile time、 、つまりCPU time = 27,3 secとの値が高くなりElapsed time = 81,9 secます。クエリの実行時間は、CPU 時間 = 0.06 秒、経過時間 = 2.8 秒のみです。その後クエリを 2 回実行すると、CPU 時間は 0.06 秒、経過時間は SQL Server の解析時間とコンパイル時間で 0.08 秒になります。

4

2 に答える 2

2

これは無駄にしか見えない

SELECT TOP (1) [Project1].[C1] AS [C1]
FROM ( SELECT CAST( [Extent7].[CreationDateTime] AS datetime2) AS [C1],
                    [Extent7].[CreationDateTime] AS [CreationDateTime]
         FROM [dbo].[OtherTable] AS [Extent7]
        WHERE [Filter1].[OrderID] = [Extent7].[OrderID]
     ) AS [Project1]
ORDER BY [Project1].[CreationDateTime] DESC

SELECT max( CAST( [Extent7].[CreationDateTime] AS datetime2) ) AS [C1]
  FROM [dbo].[OtherTable] AS [Extent7]
 WHERE [Filter1].[OrderID] = [Extent7].[OrderID]

日付を日時として保存しないのはなぜですか?

私はその外側の適用が好きではありません.
一度実行される#tempを作成し、それに参加します
.[OrderID]をPKとして宣言してください.

SELECT [Extent7].[OrderID], max( CAST( [Extent7].[CreationDateTime] AS datetime2) ) AS [C1]
FROM [dbo].[OtherTable] AS [Extent7]
GROUP BY [Extent7].[OrderID]

ループ結合が進行している可能性があります

次に、これを #temp2 に入れて、1 回だけ実行さ
れるようにします。もう一度、OrderID を PK として宣言してください。

SELECT [Extent1].[OrderID] AS [OrderID]
                     -- around 20 columns more
                     FROM [dbo].[Orders] AS [Extent1]
                     INNER JOIN -- small table
                     LEFT OUTER JOIN  -- small table
                     LEFT OUTER JOIN  -- small table
                     LEFT OUTER JOIN  -- small table
                     LEFT OUTER JOIN  -- small table
                     WHERE ([Extent1].[Status] IS NOT NULL) 
                       AND (4 = CAST( [Extent1].[Status] AS int))
                       AND ([Extent1].[SomeDateTime] IS NULL)
                       AND ([Extent1].[Report] IS NULL)

Order が 24,000 行しかない場合、数秒以上クエリを実行するというばかげたことが起こっています。

于 2013-07-10T19:09:37.290 に答える
0

頻繁に実行されるクエリの場合は、それをストアド プロシージャに変換し、プロシージャの結果を使用することをお勧めします。

Entity Framework では、プロシージャをFunction Importとしてインポートできるはずです。

次に、ストアド プロシージャにクエリ ヒントを与えるか、パラメータ スニッフィングと戦うことで、ストアド プロシージャの実行プランを制御できます。

サーバーの実行計画が 3 週間ごとに古くなり、速度が低下しているようです。

また、64 ビット SQL を使用しているとのことでした。私の経験では、64 ビット SQL はサブクエリではあまり効率的に実行されない傾向があります。私のアドバイスは、それらを避けようとすることです。

于 2013-08-07T06:52:27.133 に答える