存储过程通过Java运行速度比直接在数据库上运行慢30%

我正在使用Java 1.6,JTDS 1.2.2(也只是尝试1.2.4无效)和SQL Server 2005来创建CallableStatement来运行存储过程(没有参数)。 我看到运行相同存储过程的Java包装器比使用SQL Server Management Studio慢30%。 我运行MS SQL分析器,两个进程之间的I / O差别不大,所以我认为它与查询计划缓存无关。

存储的proc不带参数,也不返回任何数据。 它使用服务器端游标来计算填充表所需的值。

我无法看到从Java调用存储过程如何增加30%的开销,当然它只是数据库的一个管道,SQL被发送下来然后数据库执行它….数据库是否可以提供Java应用不同的查询计划?

我已发布到MSDN论坛和sourceforge JTDS论坛(主题:“在JTDS中存储过程比在DB中更直接”)我想知道是否有人有任何关于为什么会发生这种情况的建议?

提前致谢,

-詹姆士

(NB不要害怕,我会在找到解决方案后整理其他论坛中的所有答案)

Java代码段:

sLogger.info("Preparing call..."); stmt = mCon.prepareCall("SP_WB200_POPULATE_TABLE_limited_rows"); sLogger.info("Call prepared. Executing procedure..."); stmt.executeQuery(); sLogger.info("Procedure complete."); 

我已经运行了sql profiler,并发现了以下内容:

Java app:CPU:466,514阅读次数:142,478,387撰写:284,078期限:983,796

SSMS:CPU:466,973阅读次数:142,440,401写作:280,244持续时间:769,851

(两个DBCC DROPCLEANBUFFERS在分析之前运行,并且都产生正确的行数)

所以我的结论是他们都执行相同的读写操作,只是他们这样做的方式不同,你们怎么想?

事实certificate,不同客户端的查询计划明显不同(Java客户端在插入期间更新索引,而不是在更快的SQL客户端中,执行连接的方式也不同(嵌套循环Vs.收集流,嵌套循环Vs索引扫描,唉!))。 这就是为什么,我还不知道(当我到底时,我会重新发帖)

结语

我无法让它正常工作。 我尝试在Java和Mgmt studio客户端之间对连接属性( arithabortansi_nulls等)进行同质化。 它最终导致两个不同的客户端具有非常相似的查询/执行计划(但仍然具有不同的实际plan_ids)。 我发布了我在MSDN SQL Server论坛上发现的内容摘要,因为我发现不仅在JDBC客户端和管理工作室之间,而且在Microsoft自己的命令行客户端SQLCMD之间有不同的性能,我还检查了一些更激进的事情,如网络流量也可以将存储的proc包装在另一个存储过程中,只是为了咧嘴笑。

我有一种感觉问题出在游标执行方式的某个地方,并且它以某种方式导致Java进程被暂停,但是为什么不同的客户端应该在没有其他运行时产生这种不同的锁定/等待行为并且运行中的相同执行计划有点超出我的技能(我不是DBA!)。

结果,我已经决定4天就足以让任何人浪费在这样的事情上,所以我会勉强编写代码(如果我诚实的话,存储过程需要重新编码才能更多增量而不是重新编码 – 无论如何,每周计算所有数据),并将这个数据记下来体验。 我会把这个问题保持开放,非常感谢所有把帽子放在戒指上的人,这一切都很有用,如果有人想出更进一步的话,我很想听到更多的选择……如果有人发现的话这个post是因为在他们自己的环境中看到这种行为,所以希望这里有一些你可以自己尝试的指针,希望能比我们更充分地看到。

我现在已经准备好了周末!

-詹姆士

您可以为事件SQL:BatchCompleted和SP:Completed附加事件探查器和监视器,并使用持续时间> 1000的filter。从Java客户端和SSMS运行该过程。 比较两个事件的读取和写入(Java与SSMS)。 它们有显着差异吗? 这将表明相当不同的执行路径或计划,I / O存在显着差异。

还尝试捕获两者的Showplan XML事件并比较计划(将事件保存为.sqlplan文件,在SSMS中打开它以便于分析)。 他们有类似的计划吗? 估计与实际(行,倒带,重新绑定)之间是否存在巨大差异? 他们有相同程度的并行性吗? 可以从sys.dm_exec_requests视图中检索计划。

是否有任何警告事件,例如缺失列统计信息 , 排序警告 , 哈希警告 , 执行警告 , 阻止进程 ?

关键是你可以随意使用一整套调查工具。 一旦找到差异的根本原因,就可以将其追溯到Java环境设置和SSMS环境(ADO.Net SqlClient)之间的不同之处。 默认事务隔离级别,ANSI设置等等。

检查:您的问题是两个应用程序(SSMS,Java)对SQL Server进行完全相同的相同调用,并且SQL Server对每个应用程序的行为都不同? 如果是这样的话,我每隔一两年就会碰到这样的事情,而且它们会伤害我的大脑好几天。

有一次,我最终隔离了每个进程调用并记录了Profiler中整个进程的所有内容 。 我最终注意到Login事件(在TextData下)显示了大量信息,如下所示:

 -- network protocol: TCP/IP set quoted_identifier on set arithabort off set numeric_roundabort off set ansi_warnings on set ansi_padding on set ansi_nulls on set concat_null_yields_null on set cursor_close_on_commit off set implicit_transactions off set language us_english set dateformat mdy set datefirst 7 set transaction isolation level read committed 

“现有连接”事件也将显示此信息 – 但是,有时会立即发送后续调用(批次,RPC,我刚刚报告)[ ISQL或OSQL这样做,我认为 ]立即重置其中一些 – Arithabort和Quoted_Identifier似乎是最受欢迎的,其他SET选项也会根据应用程序数据库接口使用的任何连接协议的设置或要求进行修改。

另一个:一些设置在“创建”时保留为过程的属性,而其他设置在编译时被考虑在内。 一方面,连接的SET值可能被创建过程时保存的配置覆盖; 另一方面,您的两个连接可能差别很大,以至于为一个过程生成了两个执行计划。 (经过充分研究后,所有这些信息都可以在系统表和DMV中找到。)

简而言之,在我看来,SQL的晦涩难过让你搞砸了。 直到今天,我厌恶所有这些goombah设置。 我注意到的事情一直在搞乱他们[我的意思是,真的,傻瓜会为连接池设置implicit_transaction? 但是一旦他们做到了……]当地面(规则)不断从你下面改变时,很难建造结构。 毕竟,请记住那个人在沼泽中建造城堡的说法……

我记得前一段时间有过类似的问题,因为JTDS默默地将字符串参数转换为Unicode或类似的东西。 由于该转换,SQL Server无法使用从SSMS运行存储过程时使用的索引。

HIH

Java案例是否包括将结果传输到Java服务器(网络开销)以及一些Java处理? 12分钟的查询可能会产生大量数据。

如果您正在查看分析器并且执行之间没有区别,那么差异必须与客户端系统有关。

4分钟似乎只是为了准备发送声明,所以12分钟的等待必然会产生一些其他影响 – 不知道它是什么。

对不起,我没有找到正确的答案,所以我不想将其中的任何一个分配为正确,所以我要将这个答案标记为正确,并祝愿任何人遇到类似的运气!

我不确定这篇文章是否仍然相关。 我们在申请中遇到了类似的问题。

在SQL Management Studio中运行存储过程与从JDBC运行存储过程之间的一个关键区别是事务上下文。 如果在Java中使用ORM,则默认情况下,存储过程在事务上下文中运行。 在SQL管理工作室中直接运行存储过程时,事务处于关闭状态。 性能差异很大。

您是否知道Microsoft为其数据库提供了JDBC驱动程序?

这些可能更高效。

显然……你现在可能已经解决了这个问题。