为什么Microsoft SQL Server 2012查询需要花费几分钟而不是JDBC 4.0,而在Management Studio中需要几秒钟?

我正在处理从远程Microsoft SQL Server 2012到使用Microsoft JDBC Driver 4.0的Java客户端检索相对较大的ResultSet时显然是性能问题。

当我在远程服务器的Microsoft SQL Server Management Studio上运行相应的查询时,它返回约。 220k行几乎瞬间完成。 当我从客户端发出相同的查询时,它会停止。 同样的测试在客户端上运行良好,早期版本的数据库只有大约。 400行合格。

我尝试通过追加传递给DriverManager.getConnection()的URL来解决这个问题。建立连接后,我在connection.getMetaData().getURL() )的结果中看到了这个属性(以及其他几个connection.getMetaData().getURL() ,但是[ connection.getClientInfo(responseBuffering)返回null ,而且客户端仍然停滞不前。

这里可能出现什么问题,我如何指示Microsoft SQL Server(不仅仅是以Java编程方式建议)它必须以较小的块而不是一次性返回行,或者通过其他一些措施来改进JDBC查询时间。

另外两个看起来有点奇怪的观察结果可能完全指向不同的根本原因:

  • 当客户端停止时,它仍然只显示相对较轻的CPU负载,这与我对重垃圾收集的期望不同
  • “responseBuffering = adaptive”应该是现在的正常默认值

更新我已经检查过,发现从PreparedStatement切换到Statement并没有改善我的情况(显然在其他情况下可以帮助)。

更新这是我当前的查询:

 select PARENT.IDENTIFIER as PARENT_IDENTIFIER, PARENT.CLASS as PARENT_CLASS, CHILD.TYPE as CHILD_TYPE, CHILD.IDENTIFIER as CHILD_IDENTIFIER, PROPERTY.IDENTIFIER as PROPERTY_IDENTIFIER, PROPERTY.DESCRIPTION as PROPERTY_DESCRIPTION, PROPERTY.TYPE as PROPERTY_TYPE, PROPERTY.PP as PROPERTY_PP, PROPERTY.STATUS as PROPERTY_STATUS, PROPERTY.TARGET as PROPERTY_TARGET -- a date from OBJECTS as CHILD left outer join RELATIONS on RELATIONS.CHILD = CHILD.IDENTIFIER left outer join OBJECTS as PARENT on RELATIONS.PARENT = PARENT.IDENTIFIER inner join PROPERTIES as PROPERTY on PROPERTY.OBJECT = CHILD.IDENTIFIER where PROPERTY.TARGET is not null order by case when PARENT.IDENTIFIER is null then 1 else 0 end, PARENT.IDENTIFIER, CHILD.IDENTIFIER, PROPERTY.TARGET, PROPERTY.IDENTIFIER 

自适应缓冲是一个很好的答案。 我还建议通过SQL Server Profiler检查连接的SET选项。

启动跟踪时,请确保选中ExistingConnections 。 比较来自JDBC连接和SSMS连接的SPID。 我想到ARITHABORT是因为我看到了SSMS和JDBC驱动程序之间的性能差异。 微软在这里简要提到它: http : //msdn.microsoft.com/en-us/library/ms190306.aspx 。 Stack Exchange信息在这里: https : //dba.stackexchange.com/questions/9840/why-would-set-arithabort-on-dramatically-speed-up-a-query

在Oracle上,我通过在Statement / PreparedStatement对象上使用setFetchSize方法看到了巨大的影响。 显然,SQL Server驱动程序不支持该方法。 但是,驱动程序中有一个内部方法。 有关详细信息,请参阅使用JDBC驱动程序在SQL Server中设置默认行预取 。

另外,你在while (rs.next())循环中做了什么? 尝试除了读取列之外什么都不做,比如rs.getInt(1) 。 走着瞧吧。 如果它飞逝,那表明瓶颈在于您之前对结果集的处理。 如果它仍然很慢,则问题必须在驱动程序或数据库中。

您可以使用SQL Server Profiler来比较通过JDBC进行的执行以及通过SSMS运行时的执行情况。 比较CPU,读取,写入和持续时间。 如果它们不同,那么执行计划可能会有所不同,这使我回到我提到的第一件事: SET选项。

我只是打算抛弃这个建议,留给你测试。

JDBC驱动程序很可能在返回之前对所有行进行FETCHING,而另一个系统只是返回打开的游标。

我在使用JDBC的其他数据库上看到过这种行为,但是没有直接使用SQL Server的经验。

在我看过的示例中,将连接的auto commit设置为false可防止它加载整个结果集。 还有其他设置让它只加载部分等。

但这很可能是你所面临的根本问题。

我们遇到了类似的问题,原因是缓存。 我们查阅了关于查询计划的非常好的阅读。

SQL Server缓存执行计划,您可以使用以下命令查看它:

 select * from sys.dm_exec_cached_plans 

对我们有用的是忽略缓慢查询的缓存执行计划。

发生的情况可能是对于不同的查询,优化阶段会考虑查询的参数值。 因为在某些情况下使用不同的执行计划更有意义。

如果执行计划已被缓存,并且存在缓存命中(使用准备语句,其忽略参数),则执行计划对于具有不同参数的相同查询可能是次优的。

要validation这一点,您可以尝试恢复一些查询,看看是否为具有不同参数的同一查询获得了不同的执行计划。

如果事实certificate是这样的话,你可以做几件事:

  • 要立即生成结果,请在查询中添加重新编译提示OPTION (RECOMPILE) 。 这将每次重新编译查询,但可能已经比您目前拥有的要快得多。 从文档:

RECOMPILE – 指示SQL Server数据库引擎在执行后丢弃为查询生成的计划,强制查询优化器在下次执行相同查询时重新编译查询计划。 如果不指定RECOMPILE,数据库引擎会缓存查询计划并重用它们。 编译查询计划时, RECOMPILE查询提示使用查询中任何局部变量的当前值 ,如果查询位于存储过程内,则传递给任何参数的当前值。

  • 无效缓存(请参阅重新Recompiling Execution Plans )。 使缓存无效的方法是。 注意:azure中不支持其中一些
  1. 对查询引用的表或视图所做的更改(ALTER TABLE和ALTER VIEW)。

  2. 对单个过程所做的更改将从缓存中删除该过程的所有计划(ALTER PROCEDURE)。

  3. 对执行计划使用的任何索引的更改。

  4. 对执行计划使用的统计信息的更新,可以从语句(如UPDATE STATISTICS)显式生成,也可以自动生成。

  5. 删除执行计划使用的索引。

  6. 显式调用sp_recompile。

  7. 密钥的大量更改(由修改查询引用的表的其他用户的INSERT或DELETE语句生成)。

  8. 对于具有触发器的表,如果插入或删除的表中的行数显着增加。

  9. 使用WITH RECOMPILE选项执行存储过程。

  • 使用OPTIMIZE FOR提示手动优化计划(没有尝试)。

  • 分类参数后使用单独的查询。

也许,Microsoft文档中的这个链接可以帮助您解决您的问题: http : //msdn.microsoft.com/en-us/library/bb879937(v = sql.110).aspx

特别是在“ 使用自适应缓冲的指南 ”部分:

在某些情况下,使用selectMethod = cursor而不是responseBuffering = adaptive更有用,例如:

如果您的应用程序缓慢处理只进,只读结果集,例如在某些用户输入后读取每一行,则使用selectMethod = cursor而不是responseBuffering = adaptive可能有助于减少SQL Server的资源使用。

如果您的应用程序在同一连接上同时处理两个或多个只进,只读结果集,则使用selectMethod = cursor而不是responseBuffering = adaptive可能有助于减少驱动程序在处理这些结果集时所需的内存。

在这两种情况下,您都需要考虑创建,读取和关闭服务器游标的开销。