为什么远程执行查询导致它被暂停?

时间:2012-02-26 01:04:37

标签: sql-server jpa views

我提前道歉没有提供所有具体信息,但机器正在建立一个指数,可能还有很长一段时间,而且几乎完全没有反应。

我在SQL Server 2005上有一个表,列数很多,可能是20,但行数很多(数十,更可能是数亿)。为了简化我需要访问它的JPA工作量,我创建了一个包含我感兴趣的位的视图。该视图创建为:

SELECT bigtable.ID, bigtable.external_identification, mediumtable.hostname,
    CONVERT(VARCHAR, bigtable.datefield, 121) AS datefield
FROM schema.bigtable JOIN schema.mediumtable ON bigtable.joinID = mediumtable.ID;

当我想从视图中选择时,我会这样做:

SELECT * FROM vwTable WHERE external_identification = 'some string';

这在SQL Management Studio中运行良好。 external_identification列在bigtable中具有非唯一的非聚集索引。这对我们在测试环境中远程执行Java程序也很有用。现在我们距离生产还有一两天,代码已经改变了一些(虽然基本的JPA NamedQuery仍然很简单),但我们在新硬件上安装了新的SQLServer。测试版本是在32位单核机器上,新硬件是64位多核。

每当我尝试在新硬件上运行使用此视图的代码时,它会在第一次调用此查询时无限期挂起,或者如果指定了超时则超时。做了一些挖掘之后,就像:

SELECT status, command, wait_type, last_wait_type FROM sys.dm_exec_requests;

确认查询正在运行,但显示状态为:

suspended, SELECT, CXPACKET, CXPACKET
只要我关心等待它就行了。每当我从Management Studio中运行完全相同的查询时,它立即完成。所以我做了一些研究,发现这是因为等待某种并发操作开始/结束。为了避免这种情况,我将服务器范围的MAXDOP设置为1(禁用并发)。之后,查询仍然挂起,但sys.dm_exec_requests将显示:

suspended, SELECT, PAGEIOLATCH_SH, PAGEIOLATCH_SH

这表明这是某种高清/扫描问题。虽然机器的响应性比我对新硬件的预期要低,但我不希望这个查询(甚至在视图上)需要大量扫描,因为我搜索的列在底层表中被索引并且它如果我在本地运行它可以工作。但仅仅因为我没有想法并且在枪口下,我正在为视图添加索引;首先,我必须先添加唯一的聚簇索引(通过ID),然后才能尝试在external_identification上添加非唯一的非聚集索引。

我是唯一使用此数据库的人;当我从sys.dm_exec_requests中选择时,只有两个结果是我正在主动检查的查询和从sys.dm_exec_requests查询中的选择。所以它并不像是在合法的沉重,甚至在所有并发的负载下。

但我怀疑我正在抓稻草。我不是DBA,每次我不得不在查询它之外与SQL Server进行交互,这让我感到困惑。有没有人有任何想法为什么远程执行的查询会立即进入挂起状态,而本地相同的查询会立即执行?

2 个答案:

答案 0 :(得分:2)

哇,这个人直接从左侧场地抓住了我。事实证明,默认情况下,MSSQL JDBC驱动程序将其String数据类型发送为Unicode,表格/视图可能没有准备好专门处理。在我们的例子中,列和索引不是,因此MSSQL会为每次查找执行全表扫描。

在我们的测试环境中,表格足够小,这无关紧要,所以我被欺骗认为它工作正常。回想起来,我很高兴它没有 - 当计算机出现不一致的错觉时,我无法忍受。

当我将这个小参数添加到我的JDBC连接字符串的末尾时:

jdbc:sqlserver://[IP]:1433;databaseName=[db];sendStringParametersAsUnicode=false
事情立即神奇地开始起作用。对于稍微有误导性的问题(我几乎没有提到JPA)感到抱歉,但我不知道原因是什么,并且确实认为这是SQL Server方面的问题。在查询被暂停时,任务管理器没有报告大量CPU /内存使用情况,所以我只是认为它是空闲的,即使它真的是在大量磁盘使用情况下。

有关MSSQL JDBC和Unicode的更多信息,我可以在http://server.pramati.com/blog/2010/06/02/perfissues-jdbcdrivers-mssqlserver/找到我偶然发现的解决方案。谢谢,Ed,在黑暗中进行详细拍摄 - 这可能不是问题所在,但我当然学到了很多(并且很快!)关于MSSQL的坚韧部分!

答案 1 :(得分:1)

在SSMS和您的应用程序中运行的查询可能正在使用不同的查询计划 - 从您在dm_exec_requests中看到的等待类型看起来,为应用程序创建的计划正在执行表扫描SSMS计划使用索引搜索。

这是可能的,因为SSMS和应用程序数据库连接可能使用不同的连接选项,其中一些用作数据库计划缓存的密钥。

您可以通过对服务器运行默认SQL server profiler trace来找出应用程序正在使用的选项;创建连接后的第一个命令将是多个SET...选项:

SET DATEFORMAT dmy
SET ANSI_NULLS ON
...

我怀疑此列表在您的应用程序和SSMS连接之间会有所不同 - 一个常见的候选者是SET ARITHABORT {ON|OFF},因为它构成了缓存计划密钥的一部分。
如果在执行查询之前在SSMS窗口中运行SET...命令,则应该选择与应用程序使用的相同(错误)计划。

假设这证明了这个问题,下一步就是弄清楚如何防止错误计划进入缓存。由于存在一些可能的原因,因此很难给出关于如何执行此操作的一般说明 这是一种分散方法(还有其他更有针对性的方法可以尝试解决这个问题,但是他们需要更详细地了解我现在遇到的问题),但有一件事要尝试将OPTION (RECOMPILE)添加到查询结束 - 这会强制为每次执行生成一个新计划,并应防止重复使用错误计划:

SELECT * FROM vwTable WHERE external_identification = 'some string' OPTION (RECOMPILE);

假设您可以使用上述步骤复制性能不佳的SSMS,您应该可以在那里进行测试。
请注意,如果查询执行频繁(因为每次重新编译需要CPU),这会产生负面的性能影响 - 这取决于应用程序的工作负载,需要进行测试。

其他一些想法:

检查测试和生产系统之间的模式;这可能就像生产数据库中某个表中缺少索引一样简单,但考虑到SSMS查询执行正常,这是不可能的。

您应该通过关闭服务器范围MAXDOP=1来重新启用并行性,因为这会限制整个系统的性能。问题几乎肯定是查询计划,而不是并行性

您还需要注意向视图添加索引的后果 - 这样做有效地实现了视图,这将(由于表的大小)需要大量的存储开销 - 索引也需要维护在基表上发生INSERT / UPDATE / DELETE语句时。索引视图可能是不必要的,因为(从SSMS)您知道查询可以执行。