【发布时间】:2013-07-10 18:14:59
【问题描述】:
我遇到一个 SQL 数据库查询问题,突然(但通常大约每三周一次)变慢。
设置如下:
- Windows Server 2008(非 R2)64 位,8 GB RAM
- SQL Server Express 2008 R2
- 数据库大小为 6 GB(mdf 文件大小)
- 查询主要从中选择的表 (
Orders) 有大约 24000 条记录,其他五个连接表很小(100 条记录或更少) - 表
Orders有一个varbinary(MAX)列Report,其中包含平均大小约为200 到300 kB(但有时可达2 MB)的二进制数据(PDF 文档)。在这 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 应用程序)。 (默认情况下,Entity Framework 等待结果的时间似乎不会超过 30 秒。)
- 如果我将查询粘贴到 SSMS 中并让查询运行(等待 60 秒),则结果会成功返回,并且下一个相同的查询会在不到一秒的时间内再次运行。
- 大约三周后,同样的情况再次发生(但查询运行的时间将是 65 或 70 秒)
补充观察:
- 如果我在查询正常的时候重启SQL Server服务进程,进程的内存使用会慢慢增加。它在大约一周内逐步达到大约 1.5 GB(任务管理器中的私有工作集)的限制。
- 如果我在查询突然变慢时重新启动 SQL Server 服务进程并再次触发查询,我可以在任务管理器中看到该服务在几秒钟内加载了将近 1 GB。
不知何故,我怀疑整个问题与 Express 版本的内存限制 (1 GB) 和 varbinary(MAX) 列有关,尽管我只是在检查列值是否为 @987654335 的 WHERE 子句中使用它@ 或不NULL。 Report 列本身不是选定的列之一。
由于我正在违反明年最新的 Express 版本的限制(10 GB mdf 文件大小),因此我正在考虑进行更改:
- 要么将二进制列移动到另一个表并通过 FILESTREAM 将内容存储在外部,继续使用 Express Edition
- 使用没有 Express 限制的“大”SQL Server 版本之一,将二进制列保留在
Orders表中 - 两者兼得
问题:查询突然变慢的原因是什么?我计划的其中一项更改能否解决问题或是否有其他解决方案?
编辑
按照下面 cmets 中的 bhamby 提示,我在 SSMS 中设置了 SET STATISTICS TIME ON,然后再次运行查询。当查询再次变慢时,我得到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。
【问题讨论】:
-
您能隔离与这 3 周间隔一致的任何进程吗?似乎很奇怪,除了竞争之外的任何事情都会导致这种不一致的表现。
-
@GoatCO:我已经检查了好几次,但与其他进程没有竞争。频率不是确切地 3 周,它可能或多或少几天,它可能发生在早上或下午。当它发生时,服务器上既没有高 CPU 也没有内存负载。并且问题永远不会自行消失(如果其他一些竞争过程结束,我会期望)。到目前为止,我发现的唯一方法是在 SSMS 中运行一次查询。
-
啊,这很有趣。如果您不时在 SSMS 中运行它,您可以完全防止减速,还是仍然会发生?
-
首先我会去规范化并将报告放在单独的表或外部。听起来像是在内存中而不是从磁盘中读取。我只有 6 GB 的数据,其中大部分是报告。如果您从查询中取出报告,那么 1 GB 的内存不会被滥用。
-
我想知道您看到的增加的时间是否是您的查询的执行计划必须重新编译的时间。下次遇到问题时,尝试在 SSMS 中的查询前添加
SET STATISTICS TIME ON;,看看标记为SQL Server parse and compile time的部分是否真的很高。我也很想知道在您重新启动后第一次执行查询是否需要很长时间......这对我来说表明这可能是一个问题。
标签: sql sql-server tsql sql-server-2008-r2 sql-server-express