我遇到了一个 SQL 数据库查询问题,它突然(但通常大约每三周一次)变慢。
设置如下:
- Windows Server 2008(非 R2)64 位,8 GB RAM
- SQL Server 速成版 2008 R2
- 数据库大小为 6 GB(mdf 文件大小)
- 查询主要从中选择的表(
Orders
)有大约 24000 条记录,其他五个连接表很小(100 条记录或更少) - 该表
Orders
有一varbinary(MAX)
列Report
包含二进制数据(PDF 文档),平均大小约为 200 到 300 kB(但有时可能高达 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
它是由实体框架从 LINQ-to-Entities 查询生成的。查询出现在几个变体中,仅在第一WHERE
个子句中有所不同:
五种变体
WHERE ([Extent1].[Status] IS NOT NULL) AND (X = CAST( [Extent1].[Status] AS int))
X 可以在
0
和之间4
。这些查询从来都不是问题。以及两个变体 (*)
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...
在最后一行。我只有这两个查询才有下面描述的问题。
“现象”是:
- 这两个查询 (*) 每周运行 5 天,每天运行 100 到 200 次。他们用不到一秒钟的时间表演了大约三周。
- 三周后,两个查询突然需要超过 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 提示,我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 秒。之后第二次运行查询会为 SQL Server 解析和编译时间提供 0.06 秒的 CPU 时间和经过的时间 = 0.08 秒。