簡體   English   中英

SQL Server查詢突然變慢

[英]SQL Server query suddenly slow

我有一個SQL數據庫查詢的問題突然 (但通常大約每三周)變得緩慢。

安裝程序如下:

  • Windows Server 2008(不是R2)64位,8 GB RAM
  • SQL Server Express 2008 R2
  • 數據庫的大小為6 GB(文件大小為mdf)
  • 查詢表( Orders )主要是從大約24000條記錄中選擇的,其他5條連接表都很小(100條記錄以下)
  • Orders有一個varbinary(MAX)Report ,其中包含平均大小約為200到300 kB的二進制數據(PDF文檔)(偶爾可以高達2 MB)。 這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到實體查詢生成的。 查詢發生在一些變體中,這些變體僅在第一個WHERE子句中有所不同:

  • 五個變種

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

    X可以在04之間。 這些查詢從來都不是問題。

  • 這兩個變種(*)

     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...在最后一行。 我只有這兩個查詢才會遇到下面描述的問題。

“現象”是:

  • 兩個查詢(*)每天運行100到200次,每周運行5天。 他們在不到一秒鍾的時間內表演了大約三周。
  • 三周后,兩個查詢突然需要超過60秒。 (這個時間實際上會隨着數據庫大小的增加而增加。)由於超時,用戶會收到錯誤(在網頁上;它是一個Web應用程序)。 (默認情況下,實體框架似乎不會等待超過30秒的結果。)
  • 如果我將查詢粘貼到SSMS中並讓查詢運行(等待60秒),則會成功返回結果,並且下一個相同的查詢會在不到一秒的時間內再次運行。
  • 大約三周后,同樣的情況再次發生(但查詢運行的時間將是65或70秒)

另外一個觀察:

  • 如果我在查詢執行正常時重新啟動SQL Server服務進程,則進程的內存使用量會緩慢增加。 它在大約一周內逐步達到約1.5 GB(任務管理器中的私人工作集)的限制。
  • 如果我在查詢突然變慢並重新觸發查詢時重新啟動SQL Server服務進程,我可以在任務管理器中觀察該服務在幾秒鍾內加載幾乎1 GB。

不知怎的,我懷疑整個問題與Express版本和varbinary(MAX)列的內存限制(1 GB)有關,盡管我只是在WHERE子句中使用它來檢查列值是NULL還是非NULL Report列本身不是所選列之一。

由於我正在運行明年Express版本的限制(10 GB mdf文件大小),我正在考慮更改:

  • 將二進制列移動到另一個表並通過FILESTREAM在外部存儲內容,繼續使用Express Edition
  • 使用其中一個沒有Express限制的“大”SQL Server版本,將二進制列保留在Orders表中
  • 做到這兩點

問題:查詢突然變慢的原因是什么? 我計划的其中一項變更可以解決問題還是有其他解決方案?

編輯

在下面的評論中按照bhamby的提示,我在SSMS中設置了SET STATISTICS TIME ON ,然后再次運行查詢。 當查詢再次變慢時,我得到SQL Server parse and compile time的高值,即: CPU time = 27,3 secElapsed time = 81,9 sec 查詢的執行時間僅為CPU時間= 0,06秒,經過時間= 2,8秒。 在此之后第二次運行查詢,為SQL Server解析和編譯時間提供CPU時間0,06秒和經過時間= 0,08。

這看起來很浪費

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中,這樣你就可以確定它只運行一次
再次確保將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)

如果訂單只有24,000行,那么你會有一些愚蠢的事情讓你查詢超過幾秒鍾。

如果它是經常運行的查詢,那么我建議將其轉換為存儲過程並使用該過程的結果。

在Entity Framework中,您應該能夠將過程作為函數導入導入

然后,您可以通過提供查詢提示或對抗參數嗅探來控制存儲過程的執行計划。

它聞起來像你的服務器的執行計划每3周過時,因此放慢速度。

另外,您提到您使用的是64位SQL。 我的經驗是64位SQL在子查詢中不能很有效地執行。 我的建議是盡量避免它們。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM