简体   繁体   English

SQL Server查询突然变慢

[英]SQL Server query suddenly slow

I have a problem with a SQL database query that suddenly (but regularly about every three weeks) becomes slow. 我有一个SQL数据库查询的问题突然 (但通常大约每三周)变得缓慢。

Setup is the following: 安装程序如下:

  • Windows Server 2008 (not R2) 64 bit, 8 GB RAM Windows Server 2008(不是R2)64位,8 GB RAM
  • SQL Server Express 2008 R2 SQL Server Express 2008 R2
  • The database has a size of 6 GB (mdf file size) 数据库的大小为6 GB(文件大小为mdf)
  • The table ( Orders ) the query is mainly selecting from has around 24000 records, five other joined tables are small (100 records or less) 查询表( Orders )主要是从大约24000条记录中选择的,其他5条连接表都很小(100条记录以下)
  • The table Orders has a varbinary(MAX) column Report that contains binary data (PDF documents) with an average size of about 200 to 300 kB (but can be up to 2 MB occasionally). Orders有一个varbinary(MAX)Report ,其中包含平均大小约为200到300 kB的二进制数据(PDF文档)(偶尔可以高达2 MB)。 More than 90% of those 24000 orders have this column filled, for the others it is NULL , ie more than 90% of the 6 GB database size are binary data. 这24000个订单中有90%以上填充了此列,其他订单为NULL ,即6 GB数据库大小的90%以上是二进制数据。

The query in question has the following structure: 有问题的查询具有以下结构:

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

It is generated from a LINQ-to-Entities query by Entity Framework. 它是由Entity Framework从LINQ到实体查询生成的。 The query occurs in a few variations which are only different in the first WHERE clause: 查询发生在一些变体中,这些变体仅在第一个WHERE子句中有所不同:

  • The five variants 五个变种

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

    X can be between 0 and 4 . X可以在04之间。 These queries are never a problem. 这些查询从来都不是问题。

  • And the two variants (*) 这两个变种(*)

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

    or ... IS NOT NULL... in the last line. 或者... IS NOT NULL...在最后一行。 I have the problem described below only with those two queries. 我只有这两个查询才会遇到下面描述的问题。

The "phenomenon" is: “现象”是:

  • The two queries (*) are run 100 to 200 times per day, 5 days per week. 两个查询(*)每天运行100到200次,每周运行5天。 They perform with less than a second for around three weeks. 他们在不到一秒钟的时间内表演了大约三周。
  • After three weeks both queries suddenly need more than 60 seconds. 三周后,两个查询突然需要超过60秒。 (This time actually increases with growing database size.) Users get an error (on the web page; it is a web app) due to a timeout. (这个时间实际上会随着数据库大小的增加而增加。)由于超时,用户会收到错误(在网页上;它是一个Web应用程序)。 (Entity Framework doesn't seem to wait longer than 30 seconds for the result by default.) (默认情况下,实体框架似乎不会等待超过30秒的结果。)
  • If I paste the query into SSMS and let the query run (waiting the 60 seconds) the result is successfully returned and the next same query runs again in less than a second. 如果我将查询粘贴到SSMS中并让查询运行(等待60秒),则会成功返回结果,并且下一个相同的查询会在不到一秒的时间内再次运行。
  • After around three weeks the same happens again (but the time the query runs will be 65 or 70 seconds then) 大约三周后,同样的情况再次发生(但查询运行的时间将是65或70秒)

An additional observation: 另外一个观察:

  • If I restart the SQL Server service process at times when the query performs normal, memory usage of the process increases slowly. 如果我在查询执行正常时重新启动SQL Server服务进程,则进程的内存使用量会缓慢增加。 It reaches a limit of around 1,5 GB (private working set in Task Manager) step by step within roughly a week. 它在大约一周内逐步达到约1.5 GB(任务管理器中的私人工作集)的限制。
  • If I restart the SQL Server service process when the query is suddenly slow and trigger the query again I can watch in Task Manager that the service loads almost 1 GB within a few seconds. 如果我在查询突然变慢并重新触发查询时重新启动SQL Server服务进程,我可以在任务管理器中观察该服务在几秒钟内加载几乎1 GB。

Somehow I suspect the whole problem has to do with the memory limitation (1 GB) of the Express edition and the varbinary(MAX) column although I just use it in the WHERE clause that checks if the column value is NULL or not NULL . 不知怎的,我怀疑整个问题与Express版本和varbinary(MAX)列的内存限制(1 GB)有关,尽管我只是在WHERE子句中使用它来检查列值是NULL还是非NULL The Report column itself is not one of the selected columns. Report列本身不是所选列之一。

Since I am running against the limitations (10 GB mdf file size) of the Express edition next year latest I am considering changes anyway: 由于我正在运行明年Express版本的限制(10 GB mdf文件大小),我正在考虑更改:

  • Either move the binary column to another table and store the content externally via FILESTREAM, keep using the Express Edition 将二进制列移动到另一个表并通过FILESTREAM在外部存储内容,继续使用Express Edition
  • Use one of the "big" SQL Server editions without the Express limitations, keep the binary column in the Orders table 使用其中一个没有Express限制的“大”SQL Server版本,将二进制列保留在Orders表中
  • Do both 做到这两点

Question: What could be the reason that the query is suddenly slow? 问题:查询突然变慢的原因是什么? Could one of the changes I am planning solve the problem or are there other solutions? 我计划的其中一项变更可以解决问题还是有其他解决方案?

Edit 编辑

Following bhamby's tip in the comments below I've set SET STATISTICS TIME ON in SSMS before running the query again. 在下面的评论中按照bhamby的提示,我在SSMS中设置了SET STATISTICS TIME ON ,然后再次运行查询。 When the query is slow again I get a high value for SQL Server parse and compile time , namely: CPU time = 27,3 sec and Elapsed time = 81,9 sec . 当查询再次变慢时,我得到SQL Server parse and compile time的高值,即: CPU time = 27,3 secElapsed time = 81,9 sec The execution time for the query is only CPU time = 0,06 sec and Elapsed time = 2,8 sec. 查询的执行时间仅为CPU时间= 0,06秒,经过时间= 2,8秒。 Running the query a second time after that gives CPU time 0,06 sec and Elapsed time = 0,08 for the SQL Server parse and compile time. 在此之后第二次运行查询,为SQL Server解析和编译时间提供CPU时间0,06秒和经过时间= 0,08。

This just seems wasteful 这看起来很浪费

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

is

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

Why are you not storing dates as datetime? 为什么不将日期存储为日期时间?

I don't like that outer apply 我不喜欢那种外在的申请
I would create a #temp that is run once and join to it 我会创建一个运行一次的#temp并加入它
Make sure and declare [OrderID] as PK 确保并将[OrderID]声明为PK

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

You could have loop join going on 你可以进行循环连接

Next I would put this in #temp2 so that you are sure it is only run once 接下来我会把它放在#temp2中,这样你就可以确定它只运行一次
Again be sure to declare OrderID as a PK 再次确保将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)

If Order is only 24,000 rows then something stupid is going on for you to have queries more than a few seconds. 如果订单只有24,000行,那么你会有一些愚蠢的事情让你查询超过几秒钟。

If it is a query which is run often, then I would suggest turning it into a Stored Procedure and using the results of the procedure. 如果它是经常运行的查询,那么我建议将其转换为存储过程并使用该过程的结果。

In Entity Framework you should be able to import the procedure as a Function Import . 在Entity Framework中,您应该能够将过程作为函数导入导入

You can then take control of the Stored Procedure's execution plan by giving it query hints or combating Parameter Sniffing . 然后,您可以通过提供查询提示或对抗参数嗅探来控制存储过程的执行计划。

It smells like your server's execution plans are going out-of-date every 3 weeks, hence the slow-down. 它闻起来像你的服务器的执行计划每3周过时,因此放慢速度。

Also, you mentioned that you are using 64bit SQL. 另外,您提到您使用的是64位SQL。 My experience has been that 64bit SQL does not tend to perform very efficiently with sub-queries. 我的经验是64位SQL在子查询中不能很有效地执行。 My advice would be to try and avoid them. 我的建议是尽量避免它们。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM