Java 为什么Microsoft SQL Server 2012查询在JDBC 4.0上只需几分钟,而在Management Studio中只需几秒钟?

Java 为什么Microsoft SQL Server 2012查询在JDBC 4.0上只需几分钟,而在Management Studio中只需几秒钟?,java,sql-server,jdbc,sql-server-2012,Java,Sql Server,Jdbc,Sql Server 2012,在从远程Microsoft SQL Server 2012向使用Microsoft JDBC Driver 4.0的Java客户端检索相对较大的时,我正在处理一个明显的性能问题 当我在远程服务器的Microsoft SQL server Management Studio上运行相应的查询时,它几乎会立即返回大约220k行。当我从客户端发出相同的查询时,它会暂停。同样的测试在早期版本的数据库上也很好地工作,其中只有大约400行符合条件 我试图通过添加来解决这个问题;responseBufferin

在从远程Microsoft SQL Server 2012向使用Microsoft JDBC Driver 4.0的Java客户端检索相对较大的
时,我正在处理一个明显的性能问题

当我在远程服务器的Microsoft SQL server Management Studio上运行相应的查询时,它几乎会立即返回大约220k行。当我从客户端发出相同的查询时,它会暂停。同样的测试在早期版本的数据库上也很好地工作,其中只有大约400行符合条件

我试图通过添加
来解决这个问题;responseBuffering=adaptive“
到传递给
DriverManager.getConnection()
的URL。建立连接后,我在的结果中看到此属性(以及其他属性),但[返回
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

也许,Microsoft文档中的此链接可以帮助您解决问题:

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

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

如果应用程序缓慢地处理一个只读的前向结果集,例如在用户输入后读取每一行,那么使用selectMethod=cursor而不是responseBuffering=adaptive可能有助于减少SQL Server的资源使用

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


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

< P>我只想抛出这个建议,留给您进行测试。

JDBC驱动程序可能会在返回之前获取所有行,而另一个系统只是返回打开的游标

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

在我看到的示例中,将连接的自动提交设置为false会阻止它加载整个结果集


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

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

启动跟踪时,请确保选择了
ExistingConnections
。比较JDBC连接和SSMS连接中的SPID。
ARITHABORT
是我看到的导致SSMS和JDBC驱动程序之间性能差异的一种类型。Microsoft在此处简要提及:。此处堆栈交换信息:

在Oracle上,我看到了对
语句
/
PreparedStatement
对象使用
setFetchSize
方法的巨大影响。显然,SQL Server驱动程序不支持该方法。但是,驱动程序中有一个内部方法用于该方法。有关详细信息,请参阅

另外,在
while(rs.next())
循环中你在做什么?除了阅读一列之外,不要做任何事情,比如
rs.getInt(1)
。看看会发生什么。如果它飞起来,这表明瓶颈在你以前处理结果集的过程中。如果它仍然很慢,那么问题一定在驱动程序或数据库中


您可以使用SQL Server Profiler来比较通过JDBC进入的执行和通过SSMS运行的执行。比较CPU、读取、写入和持续时间。如果它们不同,则执行计划可能不同,这让我回到了我提到的第一件事:
设置
选项。

我们在事实证明,这是由于缓存。我们查阅了一本非常好的阅读资料

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

select * from sys.dm_exec_cached_plans
对我们来说,有效的方法是忽略缓存的执行计划来处理慢查询

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

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

要验证这一点,您可以尝试还原一些查询,并查看是否为具有不同参数的同一查询获得不同的执行计划

如果事实证明是这样,您可以做以下几件事:

  • 要立即获得结果,请向查询中添加重新编译提示
    选项(重新编译)
    。这将每次重新编译查询,但可能会导致