繁体   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