【问题标题】:sql server query running slow from javasql server查询从java运行缓慢
【发布时间】:2009-06-07 02:36:57
【问题描述】:

我有一个 java 程序,它对一个 sql server 数据库运行一堆查询。其中第一个查询视图返回大约 750k 条记录。我可以通过 sql server management studio 运行查询,大约 30 秒就可以得到结果。但是,我昨晚启动了程序运行。当我今天早上检查它时,大约 15 个小时后,这个查询仍然没有将结果返回给 java 程序。

我可以访问数据库来做任何我想做的事情,但我真的不知道如何开始调试它。应该怎么做才能弄清楚是什么导致了这种情况?我不是 dba,对 sql server 工具集也不是很熟悉,所以如果您能提供更多关于如何执行您可能建议的操作的详细信息,我们将不胜感激。

这是代码

stmt = connection.createStatement();
clientFeedRS = stmt.executeQuery(StringBuffer.toString());

编辑1:

好吧,已经有一段时间了,这件事被转移了,但这个问题又回来了。我考虑从 jdbc 驱动程序 v 1.2 升级到 2.0,但我们被困在 jdk 1.4 上,而 v 2.0 需要 jdk 1.5,所以这不是入门。现在我正在查看我的连接字符串属性。我看到 2 可能有用。

SelectMethod=cursor|direct
responseBuffering=adaptive|full

目前,由于延迟问题,我使用光标作为 selectMethod 运行,并且默认的 responseBuffering 已满。更改这些属性可能会有所帮助吗?如果是这样,理想的设置是什么?根据我在网上可以找到的内容,我在想,使用直接选择方法和自适应响应缓冲可能会解决我的问题。有什么想法吗?

EDIT2:

我结束了更改这两个连接字符串参数,使用默认选择方法(直接)并将 responseBuffering 指定为自适应。这最终对我来说效果最好,并缓解了我看到的延迟问题。谢谢大家的帮助。

【问题讨论】:

  • 张贴您对 jdbc 的调用将有助于找出错误(如果有的话)
  • Java 程序是什么?可能需要您如何访问数据库的代码 sn-p。此外,SQL Server Studio 是否会限制结果(例如,前 1000 行),这可能会导致结果出现偏差?
  • 如果将结果限制为 1k 行,我如何确定不是?
  • SSMS 和您的 Java 应用程序是否在同一个地方运行?如果 SSMS 在 SQL Server 上本地运行,而您的 Java 应用程序不是,则这不完全是级别比较。
  • 是的,通过 SSMS 从我运行 java 应用程序的同一个地方运行查询。

标签: java sql-server-2005 jdbc connection-string


【解决方案1】:

我有类似的问题,一个非常简单的请求 (SELECT . FROM . WHERE = .) 在 Java 中使用 jdbc 连接时最多需要 10 秒才能返回一行,而在 sqlshell 中只需要 0.01 秒。无论我使用的是官方的 MS SQL 驱动还是 JTDS 驱动,问题都是一样的。

解决方案是在 jdbc url 中设置此属性: sendStringParametersAsUnicode=false

如果您使用的是 MS SQL 官方驱动程序的完整示例:jdbc:sqlserver://yourserver;instanceName=yourInstance;databaseName=yourDBName;sendStringParametersAsUnicode=false;

如果使用不同的 jdbc 驱动程序的说明以及有关此问题的更详细信息:http://emransharif.blogspot.fr/2011/07/performance-issues-with-jdbc-drivers.html

SQL Server 将支持 Unicode 的数据类型与仅支持 ASCII 的数据类型区分开来。例如,支持 Unicode 的字符数据类型是 nchar、nvarchar、longnvarchar,其中它们的 ASCII 计数器部分分别是 char、varchar 和 longvarchar。默认情况下,所有 Microsoft 的 JDBC 驱动程序都将 Unicode 格式的字符串发送到 SQL Server,而不管 SQL Server 中定义的相应列的数据类型是否支持 Unicode。在列的数据类型支持Unicode的情况下,一切都很顺利。但是,如果列的数据类型不支持 Unicode,则会出现严重的性能问题,尤其是在数据提取期间。 SQL Server 在进行比较之前尝试将表中的非 unicode 数据类型转换为 unicode 数据类型。此外,如果非 unicode 列上存在索引,它将被忽略。这最终会导致在数据提取期间进行全表扫描,从而大大减慢搜索查询速度。

就我而言,我正在搜索的表中有 30M+ 条记录。完成请求的持续时间从 10 多秒变为应用属性后大约 0.01 秒。

希望这会对某人有所帮助!

【讨论】:

  • 非常感谢!节省了我数小时的研究时间!
  • 谢谢谢谢谢谢!这救了我的命:非常简单的查询有一个非常大的性能问题:这个参数将通过 JDBC 的查询时间从几秒变为毫秒!
  • 不错。这导致查询在不到 5 秒的时间内执行,而不是 80 秒。
  • 这是一个了不起的答案,但我只想指出,您可以通过将相关查询参数转换为 CHAR/ 来实现相同的结果无需更改驱动程序参数 VARCHAR 在查询本身中。如果您有 unicode 和 ascii 列的混合,这是唯一的选择。
【解决方案2】:

这似乎不适用于您的特定情况,但我想为搜索此问题的人提供另一种可能的解释。

我刚刚遇到了类似的问题,直接在 SQL Server 中执行的查询需要 1 分钟,而通过 java 准备好的语句执行相同的查询需要 5 分钟。我追查到它是作为准备好的声明完成的。

当您直接在 SQL Server 中执行查询时,您正在为它提供一个非参数化查询,它在优化时知道所有搜索条件。在我的例子中,我的搜索条件包括一个日期范围,SQL Server 能够查看它,决定“那个日期范围很大,我们不要使用日期索引”,然后它选择了更好的东西。

当我通过javaprepared statement执行相同的查询时,在SQL Server优化查询的时候,你还没有提供任何参数值,所以它必须猜测使用哪个索引.就我的日期范围而言,如果它针对一个小范围进行优化而我给它一个大范围,它的执行速度会比它可能的慢。同样,如果它针对大范围进行优化而我给它一个小范围,它的执行速度又会比它可能的慢。

为了证明这确实是问题所在,作为一个实验,我尝试向它提供有关使用 SQL Server 的“OPTIMIZE FOR”选项优化什么的提示。当我告诉它使用一个很小的日期范围时,我的 java 查询(实际上有一个很宽的日期范围)实际上是以前的两倍(10 分钟,而不是之前的 5 分钟,而不是 SQL Server 中的 1 分钟) )。当我告诉它我要优化的确切日期时,java 准备语句之间的执行时间是相同的。

所以我的解决方案是将确切的日期硬编码到查询中。这对我有用,因为这只是一次性声明。 PreparedStatement 不打算被重用,而只是为了参数化值以避免 SQL 注入。由于这些日期来自 java.sql.Date 对象,我不必担心我的日期值包含注入代码。

但是,对于确实需要重复使用的语句,硬编码日期是行不通的。也许更好的选择是创建针对不同日期范围优化的多个准备好的语句(一个针对一天,一个针对一周,一个针对一个月,一个针对一年,一个针对十年……或者也许您只需要 2 或 3 个选项...我不知道)然后对于每个查询,执行一个准备好的语句,其时间范围与实际查询中的范围最匹配。

当然,这只有在您的日期范围均匀分布时才有效。如果 80% 的记录在去年,而 20% 的记录分布在过去 10 年,那么“基于范围大小的多个查询”可能不是最好的。您必须根据特定范围或其他内容优化查询。你需要通过试错来解决这个问题。

【讨论】:

  • 非常感谢。在我的情况下,这被证明是缓慢的原因,特别是当我的准备语句 SQL 涉及跨数据库连接时。当我没有跨数据库连接时,使用准备好的语句的非常相似的查询没有性能问题
  • 感谢您提出这个想法!我有一些 SQL,我要插入超过 300 个值,并且我看到我的程序的运行时间超过 100,000 毫秒。但是当我使用相同的 SQL 并在 MSSQL Studio 中运行它时,它似乎立即运行了。除了 JDBC 驱动程序本身发生了一些奇怪的事情之外,我不知道为什么。我看到了这篇文章,所以我将逻辑更改为不使用所有这些变量,现在查询的运行速度快了 500 倍!速度相当于我在程序外看到的速度。太棒了。
【解决方案3】:

确保您的 JDBC 驱动程序配置为使用直接连接,而不是基于 cusror 的连接。如果您不确定,可以发布您的 JDBC 连接 URL。

确保您使用的是只进、只读的结果集(如果您没有设置,这是默认设置)。

并确保您使用的是更新的 JDBC 驱动程序。

如果所有这些都不起作用,那么您应该查看 sql profiler 并尝试在 jdbc 驱动程序执行语句时捕获 sql 查询,并在管理工作室中运行该语句,看看是否有差异。

此外,由于您要提取如此多的数据,因此您应该尝试确保 JVM 上没有任何内存/垃圾收集速度变慢(尽管在这种情况下,这并不能真正解释时间差异)。

【讨论】:

  • 你为什么说使用直接而不是基于光标?不是基于应该有助于处理大型结果集吗?
  • @shsteimer,根据 Microsoft 文档,Direct 更快。只有在需要逐行访问时才应该使用游标(或者在 JDBC 中,如果需要跨多个数据库的分布式事务,则别无选择。
【解决方案4】:

如果查询是参数化的,它可能是缺少参数或使用错误函数设置的参数,例如setLong 用于字符串等。 尝试在没有任何? 的情况下将所有参数硬编码到查询正文中运行查询,看看这是一个问题。

【讨论】:

  • 至少我对这个答案的问题有了一些线索。
【解决方案5】:

我知道这是一个老问题,但由于这是搜索此问题时的第一个结果,我想我应该发布对我有用的内容。当我使用 SQL Server JDBC 驱动程序时,我的查询用时不到 10 秒,但在使用 jTDS 时用时超过 4 分钟。我尝试了这里提到的所有建议,但没有任何区别。唯一有效的是将其添加到 URL ";prepareSQL=1"

更多信息请见Here

【讨论】:

    【解决方案6】:

    我知道这是一个非常古老的问题,但由于这是搜索此问题时的第一个结果之一,我认为我应该发布对我有用的内容。

    当我使用 SQL Server Management Studio (SSMS) 时,我有一个查询大约需要 3 秒,但在通过 executeQuery 方法使用 jTDS JDBC 驱动程序运行时需要 3.5 分钟。

    上面提到的建议都没有对我有用,主要是因为我只使用了 Statement 而不是 Prepared Statement。唯一对我有用的是在连接字符串中指定初始或默认数据库的名称,连接用户至少具有 db_datareader 数据库角色成员资格。只有 public 角色不够

    这是示例连接字符串:

    jdbc:jtds:sqlserver://YourSqlServer.name:1433/DefaultDbName
    

    请确保您在连接字符串中指定了结尾 /DefaultDbName。这里DefaultDbName 是数据库的名称,指定用于建立JDBC 连接的用户ID 至少具有db_datareader 数据库角色。如果省略,SQL Server 默认使用master 数据库。如果用于建立 JDBC 连接的用户 ID 在 master 数据库中只有public 角色,则查询时间异常长。

    我不知道为什么会这样。但是,我知道在这种情况下使用了不同的查询计划。我使用 SQL Profiler 工具确认了这一点。

    环境详情:

    • SQL Server 版本:2016
    • jTDS 驱动版本:1.3.1
    • Java 版本:11

    【讨论】:

      【解决方案7】:

      提取这么多数据将需要大量时间。您可能应该想办法在任何给定时间在您的应用程序中不需要那么多数据。例如,分页数据或使用lazy loading。如果没有更多关于您要完成的工作的详细信息,很难说。

      【讨论】:

      • 我可以接受很长一段时间,我什至可以接受几分钟,几十分钟也可以,但在我看来,几个小时就是奇怪的事情。
      • 你在盒子上的记忆力如何?
      【解决方案8】:

      从管理工作室运行时它很快的事实可能是由于不正确的缓存查询计划和过时的索引(例如,由于大量导入或删除)。是否在 SSMS 中快速返回所有 750K 记录?

      尝试重建您的索引(或者如果这需要太长时间,请更新您的统计信息);并且可能刷新过程缓存(如果这是生产系统,请谨慎使用...):DBCC FREEPROCCACHE

      【讨论】:

      • 您能否详细说明一下。我正在努力解决 similar 性能问题,但尚未找到解决方案。
      【解决方案9】:

      要开始调试,最好确定问题区域是在数据库中还是在应用程序中。您是否尝试过更改查询以使其返回更小的结果?如果那没有返回,我建议针对您从 Java 访问数据库的方式。

      【讨论】:

        【解决方案10】:

        尝试调整Statement的fetch size,试试cursor的selectMethod

        http://technet.microsoft.com/en-us/library/aa342344(SQL.90).aspx

        我们在使用 mysql 时遇到了大型结果集的问题,需要使其流式传输结果集,如以下链接中所述。

        http://helpdesk.objects.com.au/java/avoiding-outofmemoryerror-with-mysql-jdbc-driver

        【讨论】:

          【解决方案11】:

          引用自 MS 自适应缓冲区指南:

          避免使用连接字符串属性 selectMethod=cursor 以允许应用程序处理非常大的结果集。自适应缓冲功能允许应用程序在不使用服务器游标的情况下处理非常大的只进、只读结果集。请注意,当您设置 selectMethod=cursor 时,该连接生成的所有只进、只读结果集都会受到影响。换句话说,如果您的应用程序经常处理包含几行的短结果集,那么为每个结果集创建、读取和关闭服务器游标将在客户端和服务器端使用比使用 selectMethod 的情况更多的资源未设置为光标。

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

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

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

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

          查看更多:http://technet.microsoft.com/en-us/library/bb879937.aspx

          【讨论】:

            【解决方案12】:

            有时可能是由于参数绑定到查询对象的方式。 从java程序执行时,我发现以下代码非常慢。

            Query query = em().createNativeQuery(queryString)                    
                            .setParameter("param", SomeEnum.DELETED.name())
            

            一旦我删除“deleted”参数并直接将该“DELETED”字符串附加到查询中,它变得非常快。这可能是因为 SQL Server 期望绑定所有参数来决定优化计划。

            【讨论】:

              【解决方案13】:

              SQLWB 是否需要类似的时间?如果 Java 版本慢得多,那么我会检查几件事:

              1. 您应该使用只进、只读的 ResultSet 获得最佳性能。
              2. 我记得 MSFT 的旧 JDBC 驱动程序很慢。确保您使用的是最新的 n-greatest。我认为有一个通用的 SQL Server 和一个专门用于 SQL 2005。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2023-04-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2021-05-16
                相关资源
                最近更新 更多