Каковы вероятные причины проблем производительности StringBuilder и ResultSet - PullRequest
2 голосов
/ 02 февраля 2010

Я перебираю ResultSet в Java; который для целей тестирования возвращает около 30 строк с 17 столбцами (все данные String) на строку. Я вручную строю XML-строку из результатов, используя StringBuilder, и для выполнения этих итераций цикл буквально занимает около 36 секунд.

Примечание. Я понимаю, что это не лучший способ извлечения XML из базы данных или даже не лучший способ получить XML из ResultSet - но это заставляет меня задуматься о низкой производительности независимо от этого.

Обновление: Согласно ответам, я должен обратиться к следующему: Время выполнения запроса меньше секунды, и я сделал 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
}

Обновление: Как и предполагалось, я закрою этот вопрос, поскольку заголовок не отражает направление, в котором возникла проблема.

Ответы [ 9 ]

6 голосов
/ 02 февраля 2010

Я думаю, что ваша заметка2 говорит сама за себя.

Время не теряется в StringBuilder, а где-то еще ...


columnName = rsmeta.getColumnName(i+1);

Чтение метаданных можетбыть очень медленным, в зависимости от реализации.Вы можете прочитать их только один раз для всех наборов результатов и повторно использовать их в цикле.


ОБНОВЛЕНИЕ

Из вашего последнего обновления StringBuilder больше не беспокоити проблема в ResultSet.Я чувствую, что название вопроса и все приведенные ответы не совпадают с вашей текущей озабоченностью.
Я предлагаю закрыть этот вопрос и открыть новый для новой озабоченности : -)

5 голосов
/ 02 февраля 2010

Я сильно сомневаюсь, что StringBuilder является вашим узким местом по сравнению со временем, затрачиваемым на доступ и получение данных из базы данных. Тот факт, что оптимизация простой конкатенации строк существенно не изменила время выполнения, подтверждает это.

Вам нужно обратить внимание на оптимизацию доступа к базе данных - более быстрое соединение с БД, сжатое соединение и т. Д.

Однако я могу предложить одну микрооптимизацию для вашего кода: вместо добавления односимвольных строк, таких как «<», добавьте символ, например «<». Это, однако, не должно иметь большого значения. </p>

3 голосов
/ 02 февраля 2010

Я очень сомневаюсь, что StringBuilder является виновником здесь. Java широко использует его, я широко его использую, я переписал его для своей собственной разновидности JVM, и в основном он всегда может съедать символы сотнями миллионов в секунду.

Я думаю, что ваша проблема связана с самим доступом к базе данных. Когда вы запускаете запрос и получаете ResultSet, это не обязательно означает, что все данные были получены и внутренне преобразованы в простое в управлении представление памяти. В зависимости от реализации базы данных (и ее драйвера JDBC), ResultSet может быть обещанием ряда результатов, которые динамически выбираются при вызове методов ResultSet.next() и ResultSet.getString().

Попробуйте просто изучить результаты, позвонив всем своим next() и getString(), но не сохраняя полученные данные в своем StringBuilder. Если это все еще занимает 36 секунд, то StringBuilder невиновен (и я убежден, что это так).

2 голосов
/ 04 февраля 2010

Проверьте, как вы получаете результат обратно. В операторе есть методы, которые позволяют изменить способ получения данных ResultSet.

В частности, посмотрите на

2 голосов
/ 02 февраля 2010

Проблема в том, что ResultSet имеет постоянную ссылку на базу данных и получает больше информации при ее вызове. Могу ли я предложить вам взглянуть на CachedRowSet (javadoc здесь ). Он немедленно извлечет все данные и будет действовать точно так же, как и ResultSet. Затем вы можете закрыть соединение с базой данных, а затем начать анализ данных. Я бы предложил попробовать это и посмотреть, ускорит ли это ваш процесс.

2 голосов
/ 02 февраля 2010

На самом деле чтение информации из набора результатов с помощью getColumnName (), next () и getValue () часто занимает намного больше времени , чем выборка результата в первую очередь. Это особенно верно для не прокручиваемых наборов результатов.

StringBuilder распределяет память экспоненциально (newSize = FACTOR * oldSize), поэтому чем больше вы делаете, тем меньше нужно перераспределять.

Чтобы действительно проверить это, просто замените rset и rsmeta на фиктивный объект, который имеет те же методы: пусть next () возвращает true для реалистического числа строк и имеет другие методы возвращают строки реалистичной длины.

1 голос
/ 02 февраля 2010

Попробуйте заменить различные звонки консервированными данными. Например, кто-то предположил, что доступ к метаданным является виновником. Попробуйте заменить:

columnName = rsmeta.getColumnName(i+1);

с:

columnName = "Column" + i;

где i - это int, который вы устанавливаете в 0 перед циклом и увеличиваете по циклу.

1 голос
/ 02 февраля 2010

Я согласен с тем, что StringBuilder не может быть здесь в реальном времени. Тем не менее, StringBuffer может быть немного ограничен, поскольку в итоге ему приходится выделять дополнительное пространство по мере увеличения буфера (и его размера по умолчанию). Это предложение касается вашего вопроса, но я сомневаюсь, что они решат вашу проблему.

Удачи

0 голосов
/ 20 октября 2010

Вчера у меня была похожая проблема: запрос к серверу Microsoft SQL Server 10 около 1,7 миллиона записей и чтение значений заняли около 50 минут.После изменения;около 80 секунд ...

Моя проблема заключалась в том, что использовался драйвер jdbc: единственное изменение - переход с драйвера com.microsoft.sqlserver.jdbc версии 1.2 на драйвер net.sourceforge.jtds версии 1.2.2 ...

...