2

我在 Java 中循环一个 ResultSet;出于测试目的,它返回大约 30 行,每行 17 列(所有字符串数据)。我正在使用 StringBuilder 从结果中手动构建一个 XML 字符串,循环完成这些迭代实际上需要大约 36 秒。

注意:我意识到这不是从数据库中获取 XML 的最佳方法,甚至不是从 ResultSet 中获取 XML 的最佳方法 - 但这让我对性能缓慢感到好奇。

更新:根据到目前为止的回复,我必须解决以下问题:运行查询的时间不到一秒,并且我在代码的每个部分之前和之后都做了一个 System.currentTimeMillis() 来缩小范围。36 秒完全在下面的代码中。

ResultSetMetaData rsmeta = rset.getMetaData();
StringBuilder resultBuilder = new StringBuilder();
resultBuilder.append("<?xml version=\"1.0\" ?><ROWSET>");
if(numColumns != 0){   
   while (rset.next()) {
      resultBuilder.append("<ROW>");
      for (int i = 0; i <= numColumns -1;i++) {
         columnName = rsmeta.getColumnName(i+1);
         resultBuilder.append("<");
         resultBuilder.append(columnName);
         resultBuilder.append(">");
         resultBuilder.append(rset.getString(i+1));
         resultBuilder.append("</");
         resultBuilder.append(columnName);
         resultBuilder.append(">");
      }
      resultBuilder.append("</ROW>");
      numRows += 1;
   }
}
else {
   stmt.close();
   wsConn.close();
   return "No Results";
}

更新:鉴于我收到的建议 - 这段代码花费的时间大致相同或花费半秒。

StringBuilder resultBuilder = new StringBuilder();
resultBuilder.append("<?xml version=\"1.0\" ?><ROWSET>");
if(numColumns != 0){   
   while (rset.next()) {
      resultBuilder.append("<ROW>");
      for (int i = 0; i <= numColumns -1;i++) {
         //columnName = rsmeta.getColumnName(i+1);
         resultBuilder.append("<");
         resultBuilder.append("TestColumnName");
         resultBuilder.append(">");
         //resultBuilder.append(rset.getString(i+1));
         resultBuilder.append("TestData");
         resultBuilder.append("</");
         resultBuilder.append("TestColumnName");
         resultBuilder.append(">");
      }
      resultBuilder.append("</ROW>");
      numRows += 1;
   }
}
else {
   stmt.close();
   wsConn.close();
   return "No Results";
}  

我做的最后一个测试消除了所有其他内容,是用实际数量的迭代替换 while 测试(160,从我之前完成的小测试返回的最大行数)。现在的问题是,这个结果集会导致如此缓慢的原因是什么。

while (numRows <= 160) {
// same as above
}

更新:正如建议的那样,我将关闭这个问题,因为标题没有反映问题的方向。

4

9 回答 9

6

我认为你的note2会自己说话。

时间并没有在 StringBuilder 中丢失,而是在其他地方......


columnName = rsmeta.getColumnName(i+1);

读取元数据可能会非常慢,具体取决于实现。对于所有结果集,您只能读取一次,然后在循环中重用它们。


更新

从您上次更新开始,StringBuilder 已不再受关注,而问题出在 ResultSet 上。我觉得问题的标题和给出的所有答案都与您当前的关注点不同步。
我建议关闭这个问题,并为新的关注打开一个新问题:-)

于 2010-02-02T15:16:04.590 回答
5

与从数据库访问和检索数据所花费的时间相比,我强烈怀疑 StringBuilder 是您的瓶颈。优化简单字符串连接并没有显着改变运行时间的事实证实了这一点。

您需要考虑优化访问数据库的方式——更快地连接到数据库、压缩连接等。

但是,我可以为您的代码提供一种微优化:不要附加像“<”这样的单字符串,而是附加一个字符,如“<”。然而,这不应该有太大的区别。

于 2010-02-02T15:16:54.377 回答
3

我非常怀疑这StringBuilder是这里的罪魁祸首。Java 广泛使用它,我已经广泛使用它,我已经为我自己的 JVM 重写了它,基本上它总是能够以每秒数亿的速度吃掉字符。

我认为您的麻烦来自数据库访问本身。当您运行查询并获得 aResultSet时,并不一定意味着所有数据都已获得并在内部转换为易于管理的内存表示。根据数据库实现(及其 JDBC 驱动程序),ResultSet可能是许多结果的承诺ResultSet.next(),这些结果在调用和ResultSet.getString()方法时动态获取。

尝试简单地探索结果,调用你所有的next()and getString(),但不要将获得的数据存储在你的StringBuilder. 如果仍然需要 36 秒,那么StringBuilder是无辜的(我坚信它是无辜的)。

于 2010-02-02T16:46:46.320 回答
2

实际上,使用 getColumnName()、next() 和 getValue() 从结果集中读取信息通常比首先获取结果要花费更多时间。对于不可滚动的结果集尤其如此。

StringBuilder 以指数方式分配内存(newSize = FACTOR*oldSize),所以你做的越多,它需要重新分配的就越少。

要真正测试这一点,只需将rsetrsmeta替换为具有相同方法的虚拟对象:让 next() 针对实际数量的行返回 true,并让其他方法返回实际长度的字符串。

于 2010-02-02T16:15:50.373 回答
2

问题是它ResultSet有一个到数据库的持久链接,并在调用它时获得更多信息。我可以建议你看看CachedRowSet(javadoc here)。它将立即拉下所有数据,并完全像ResultSet其他情况一样。然后,您可以关闭数据库连接,然后开始解析数据。我建议尝试一下,看看它是否会加快您的流程。

于 2010-02-02T17:09:46.910 回答
2

检查您如何取回结果集。语句中有一些方法允许您更改 ResultSet 提取数据的方式。

特别是看

于 2010-02-04T02:21:28.973 回答
1

我同意 StringBuilder 可能不是这里的实时接收器。但是,StringBuffer 可能会受到轻微限制,因为随着缓冲区的增长(并超过其默认大小),它最终不得不分配额外的空间。这个建议解决了你的问题,但我再次怀疑他们会解决你的问题。

祝你好运

于 2010-02-02T15:21:10.710 回答
1

尝试用固定数据替换各种调用。例如,有人建议访问元数据是罪魁祸首。尝试更换:

columnName = rsmeta.getColumnName(i+1);

和:

columnName = "Column" + i;

其中 i 是您在循环之前设置为 0 并在循环中递增的 int。

于 2010-02-02T16:13:21.580 回答
0

昨天我遇到了类似的问题:查询 Microsoft SQL Server 10 的大约 170 万条记录并读取这些值大约需要 50 分钟。改变后;大约80秒...

我的问题是使用的 jdbc 驱动程序:唯一的变化是从 com.microsoft.sqlserver.jdbc 1.2 版驱动程序到 net.sourceforge.jtds 1.2.2 版驱动程序......

于 2010-10-20T07:12:37.923 回答